WebGL:淡入淡出绘图缓冲区 [英] WebGL: fade drawing buffer

查看:48
本文介绍了WebGL:淡入淡出绘图缓冲区的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已将 preserveDrawingBuffer 设置为 true.这样做会立即看到缓冲区上绘制的所有内容,但是,我想知道是否有办法随着时间的推移以某种方式淡化缓冲区,以便绘制的旧元素随着时间的推移消失,而最新绘制的元素以相对较高的不透明度出现,直到它们也消失.

有没有更好的方法来达到这样的效果?

我已经尝试通过降低不透明度直到达到 0 来再次渲染以前的元素,但这似乎不是一种有效的淡化方式,因为一旦绘制了某些东西我不打算改变它.

谢谢!

解决方案

这其实很常见,只是重绘我在这里过的东西

WebGL:从画布中平滑淡出线条

重绘内容意味着您可以防止某些内容不褪色.例如,如果您正在制作太空射击游戏,并且您只希望爆炸和导弹轨迹淡出,但又不想让宇宙飞船和小行星淡出,那么您需要通过重新绘制所有内容并手动淡出内容来实现在降低它们的 alpha 的同时绘制它们

如果您只想让所有内容淡出,那么您可以使用后期处理类型的效果.

您制作了 2 个纹理并将它们附加到 2 个帧缓冲区.您使用

将第一个帧缓冲区 fadeFb1 混合/淡化到第二个 fadeFb2 和淡入淡出颜色

gl_FragColor = mix(textureColor,fadeColor,mixAmount);

然后您将任何新内容绘制到 fadeFb2

然后最后将 fadeFb2 绘制到画布上,以便您可以看到结果.

下一帧你做同样的事情,除了交换你正在绘制的缓冲区和你正在淡入的缓冲区.

frame 0: mix(fadeFb1,fadeColor)->fadeFb2,draw->fadeFb2,fadeFB2->canvas第一帧:mix(fadeFb2,fadeColor)->fadeFb1,draw->fadeFb1,fadeFB1->canvas第 2 帧:mix(fadeFb1,fadeColor)->fadeFb2,绘制->fadeFb2,fadeFB2->canvas...

请注意,您在绘制时不清除,因为您需要将结果留在后面

至于设置帧缓冲区,这里有一个可能有用的教程

http://webglfundamentals.org/webgl/lessons/webgl-image-processing-continued.html

这是一个使用 twgl 的例子,因为我懒得直接使用 WebGL

var vs = `属性 vec4 位置;统一 mat4 u_matrix;无效主(){gl_Position = u_matrix * 位置;}`;var fs = `精密中等浮点数;统一 vec4 u_color;无效主(){gl_FragColor = u_color;}`;var vsQuad = `属性 vec4 位置;属性 vec2 texcoord;不同的 vec2 v_texcoord;无效主(){gl_Position = 位置;v_texcoord = texcoord;}`;var fsFade = `精密中等浮点数;不同的 vec2 v_texcoord;统一的 sampler2D u_texture;统一浮动 u_mixAmount;统一 vec4 u_fadeColor;无效主(){vec4 color = texture2D(u_texture, v_texcoord);gl_FragColor = 混合(颜色,u_fadeColor,u_mixAmount);}`;var fsCopy = `精密中等浮点数;不同的 vec2 v_texcoord;统一的 sampler2D u_texture;无效主(){gl_FragColor = texture2D(u_texture, v_texcoord);}`;var $ = document.querySelector.bind(document);var mixAmount = 0.05;var mixElem = $("#mix");var mixValueElem = $("#mixValue");mixElem.addEventListener('input', function(e) {setMixAmount(e.target.value/100);});函数 setMixAmount(value) {mixAmount = 值;mixValueElem.innerHTML = mixAmount;}setMixAmount(mixAmount);var gl = $("canvas").getContext("webgl");var m4 = twgl.m4;var programInfo = twgl.createProgramInfo(gl, [vs, fs]);变量淡出程序信息 = twgl.createProgramInfo(gl, [vsQuad, fsFade]);var copyProgramInfo = twgl.createProgramInfo(gl, [vsQuad, fsCopy]);//创建一个 -1 到 +1 的四边形var quadBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);//创建一个 RGBA/UNSIGNED_BYTE 纹理和深度缓冲帧缓冲var imgFbi = twgl.createFramebufferInfo(gl);//创建 2 个 RGBA 纹理 + 深度帧缓冲区var淡入淡出= [{ 格式:gl.RGBA,最小值:gl.NEAREST,最大值:gl.NEAREST,包裹:gl.CLAMP_TO_EDGE,},{ 格式:gl.DEPTH_STENCIL },];varfadeFbi1 = twgl.createFramebufferInfo(gl,fadeAttachments);varfadeFbi2 = twgl.createFramebufferInfo(gl,fadeAttachments);函数 drawThing(gl,x,y,旋转,缩放,颜色){var matrix = m4.ortho(0, gl.canvas.width, gl.canvas.height, 0, -1, 1);矩阵 = m4.translate(matrix, [x, y, 0]);矩阵 = m4.rotateZ(矩阵,旋转);矩阵 = m4.scale(matrix, [scale, scale, 1]);gl.useProgram(programInfo.program);twgl.setBuffersAndAttributes(gl, programInfo, quadBufferInfo);twgl.setUniforms(程序信息,{u_matrix:矩阵,u_color:颜色,});twgl.drawBufferInfo(gl, gl.TRIANGLES, quadBufferInfo);}函数兰特(最小,最大){如果(最大 === 未定义){最大值 = 最小值;分钟 = 0;}return min + Math.random() * (max - min);}函数渲染(时间){如果(twgl.resizeCanvasToDisplaySize(gl.canvas)){twgl.resizeFramebufferInfo(gl,fadeFbi1,fadeAttachments);twgl.resizeFramebufferInfo(gl,fadeFbi2,fadeAttachments);}//通过使用mixAmount从fadeFbi1复制到fabeFbi2来淡入淡出.//fadeFbi2 将包含 mix(fadeFb1, u_fadeColor, u_mixAmount)twgl.bindFramebufferInfo(gl,fadeFbi2);gl.useProgram(fadeProgramInfo.program);twgl.setBuffersAndAttributes(gl,fadeProgramInfo,quadBufferInfo);twgl.setUniforms(淡入淡出程序信息,{u_texture:fadeFbi1.attachments[0],u_mixAmount:混合量,u_fadeColor: [0, 0, 0, 0],});twgl.drawBufferInfo(gl, gl.TRIANGLES, quadBufferInfo);//现在绘制新的东西到fadeFb2.注意我们没有清除!twgl.bindFramebufferInfo(gl,fadeFbi2);var x = rand(gl.canvas.width);var y = rand(gl.canvas.height);var 旋转 = rand(Math.PI);var scale = rand(10, 20);var color = [rand(1), rand(1), rand(1), 1];drawThing(gl,x,y,旋转,比例,颜色);//现在将fadeFbi2复制到画布上,以便我们可以看到结果twgl.bindFramebufferInfo(gl, null);gl.useProgram(copyProgramInfo.program);twgl.setBuffersAndAttributes(gl, copyProgramInfo, quadBufferInfo);twgl.setUniforms(copyProgramInfo, {u_texture:fadeFbi2.attachments[0],});twgl.drawBufferInfo(gl, gl.TRIANGLES, quadBufferInfo);//交换变量,以便我们下次渲染到相反的纹理无功温度=fadeFbi1;淡出Fbi1 = 淡出Fbi2;fadeFbi2 = 温度;请求动画帧(渲染);}requestAnimationFrame(render);

body { margin: 0;}画布{显示:块;宽度:100vw;高度:100vh;}#ui { 位置:绝对;顶部:0 }

<script src="https://twgljs.org/dist/twgl-full.min.js"></脚本><canvas></canvas><div id="ui"><span>mix:</span><input id="mix" type="range" min="0" max="100" value="5"/<span id="mixValue"></span>

I've set preserveDrawingBuffer to true. Doing this results in everything drawn on the buffer to be seen all at once, however, I was wondering if there is a way to somehow fade the buffer as time goes on so that the old elements drawn disappear over time, and the newest drawn elements appear with a relatively high opacity until they also fade away.

Is there a better way to achieve such an effect?

I've tried to render previous elements again by lowering their opacity until it reaches 0 but it didn't seem like an efficient way of fading as once something is drawn I don't plan on changing it.

Thanks!

解决方案

It's actually common it just redraw stuff which I went over here

WebGL: smoothly fade lines out of canvas

Redrawing stuff means you can keep some things from not fading out. For example if you're making a space shooting game and you only want explosions and missile trails to fade out but you don't want the spaceships and asteroids to fade out then you need to do it by redrawing everything and manually fading stuff out by drawn them while decreasing their alpha

If you just want everything to fade out then you can use a post processing type effect.

You make 2 textures and attach them to 2 framebuffers. You blend/fade the first framebuffer fadeFb1 into the second one fadeFb2 with a fadeColor using

gl_FragColor = mix(textureColor, fadeColor, mixAmount);

You then draw any new stuff to fadeFb2

Then finally draw fadeFb2 to the canvas so you can see the result.

The next frame you do the same thing except swap which buffer you're drawing to and which one you're fading to.

frame 0: mix(fadeFb1,fadeColor)->fadeFb2, draw->fadeFb2, fadeFB2->canvas
frame 1: mix(fadeFb2,fadeColor)->fadeFb1, draw->fadeFb1, fadeFB1->canvas
frame 2: mix(fadeFb1,fadeColor)->fadeFb2, draw->fadeFb2, fadeFB2->canvas
...

Note you don't clear when you draw since you need the result to be left behind

As for setting up framebuffers there's a tutorial here that might be useful

http://webglfundamentals.org/webgl/lessons/webgl-image-processing-continued.html

Here's an example using twgl since I'm too lazy for straight WebGL

var vs = `
attribute vec4 position;

uniform mat4 u_matrix;

void main() {
  gl_Position = u_matrix * position;
}
`;

var fs = `
precision mediump float;

uniform vec4 u_color;

void main() {
  gl_FragColor = u_color;
}
`;
var vsQuad = `
attribute vec4 position;
attribute vec2 texcoord;

varying vec2 v_texcoord;

void main() {
  gl_Position = position;
  v_texcoord = texcoord;
}
`;
var fsFade = `
precision mediump float;

varying vec2 v_texcoord;

uniform sampler2D u_texture;
uniform float u_mixAmount;
uniform vec4 u_fadeColor;

void main() {
  vec4 color = texture2D(u_texture, v_texcoord);
  gl_FragColor = mix(color, u_fadeColor, u_mixAmount);
}
`;
var fsCopy = `
precision mediump float;

varying vec2 v_texcoord;

uniform sampler2D u_texture;

void main() {
  gl_FragColor = texture2D(u_texture, v_texcoord);
}
`;

var $ = document.querySelector.bind(document);

var mixAmount = 0.05;
var mixElem = $("#mix");
var mixValueElem = $("#mixValue");
mixElem.addEventListener('input', function(e) {
    setMixAmount(e.target.value / 100);
});

function setMixAmount(value) {
  mixAmount = value;
  mixValueElem.innerHTML = mixAmount;
}
setMixAmount(mixAmount);

var gl = $("canvas").getContext("webgl");
var m4 = twgl.m4;
var programInfo = twgl.createProgramInfo(gl, [vs, fs]);
var fadeProgramInfo = twgl.createProgramInfo(gl, [vsQuad, fsFade]);
var copyProgramInfo = twgl.createProgramInfo(gl, [vsQuad, fsCopy]);

// Creates a -1 to +1 quad
var quadBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);

// Creates an RGBA/UNSIGNED_BYTE texture and depth buffer framebuffer
var imgFbi = twgl.createFramebufferInfo(gl);

// Creates 2 RGBA texture + depth framebuffers
var fadeAttachments = [
  { format: gl.RGBA, min: gl.NEAREST, max: gl.NEAREST, wrap: gl.CLAMP_TO_EDGE, },
  { format: gl.DEPTH_STENCIL },
];
var fadeFbi1 = twgl.createFramebufferInfo(gl, fadeAttachments);
var fadeFbi2 = twgl.createFramebufferInfo(gl, fadeAttachments);

function drawThing(gl, x, y, rotation, scale, color) {
  var matrix = m4.ortho(0, gl.canvas.width, gl.canvas.height, 0, -1, 1);
  matrix = m4.translate(matrix, [x, y, 0]);
  matrix = m4.rotateZ(matrix, rotation);
  matrix = m4.scale(matrix, [scale, scale, 1]);

  gl.useProgram(programInfo.program);
  twgl.setBuffersAndAttributes(gl, programInfo, quadBufferInfo);
  twgl.setUniforms(programInfo, {
    u_matrix: matrix,
    u_color: color,
  });
  twgl.drawBufferInfo(gl, gl.TRIANGLES, quadBufferInfo);
}

function rand(min, max) {
  if (max === undefined) {
    max = min;
    min = 0;
  }
  return min + Math.random() * (max - min);
}

function render(time) {
  if (twgl.resizeCanvasToDisplaySize(gl.canvas)) {
    twgl.resizeFramebufferInfo(gl, fadeFbi1, fadeAttachments);
    twgl.resizeFramebufferInfo(gl, fadeFbi2, fadeAttachments);
  }
  
  // fade by copying from fadeFbi1 into fabeFbi2 using mixAmount.
  // fadeFbi2 will contain mix(fadeFb1, u_fadeColor, u_mixAmount)
  twgl.bindFramebufferInfo(gl, fadeFbi2);

  gl.useProgram(fadeProgramInfo.program);
  twgl.setBuffersAndAttributes(gl, fadeProgramInfo, quadBufferInfo);
  twgl.setUniforms(fadeProgramInfo, {
    u_texture: fadeFbi1.attachments[0],
    u_mixAmount: mixAmount,
    u_fadeColor: [0, 0, 0, 0],
  });
  twgl.drawBufferInfo(gl, gl.TRIANGLES, quadBufferInfo);

  // now draw new stuff to fadeFb2. Notice we don't clear!
  twgl.bindFramebufferInfo(gl, fadeFbi2);

  var x = rand(gl.canvas.width);
  var y = rand(gl.canvas.height);
  var rotation = rand(Math.PI);
  var scale = rand(10, 20);
  var color = [rand(1), rand(1), rand(1), 1];
  drawThing(gl, x, y, rotation, scale, color);


  // now copy fadeFbi2 to the canvas so we can see the result
  twgl.bindFramebufferInfo(gl, null);

  gl.useProgram(copyProgramInfo.program);
  twgl.setBuffersAndAttributes(gl, copyProgramInfo, quadBufferInfo);
  twgl.setUniforms(copyProgramInfo, {
    u_texture: fadeFbi2.attachments[0],
  });
  twgl.drawBufferInfo(gl, gl.TRIANGLES, quadBufferInfo);

  // swap the variables so we render to the opposite textures next time
  var temp = fadeFbi1;
  fadeFbi1 = fadeFbi2;
  fadeFbi2 = temp;

  requestAnimationFrame(render);
}
requestAnimationFrame(render);

body { margin: 0; }
canvas { display: block; width: 100vw; height: 100vh; }
#ui { position: absolute; top: 0 }

<script src="https://twgljs.org/dist/twgl-full.min.js"></script>
<canvas></canvas>
<div id="ui">
<span>mix:</span><input id="mix" type="range" min="0" max="100" value="5" /><span id="mixValue"></span>
</div>

这篇关于WebGL:淡入淡出绘图缓冲区的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆