WebGL:淡化绘图缓冲区

时间:2016-07-15 18:03:52

标签: webgl

我已将preserveDrawingBuffer设为true。 这样做会导致缓冲区上绘制的所有内容同时被看到,但是, 我想知道是否有一种方法可以随着时间的推移以某种方式淡化缓冲区,以便绘制的旧元素随着时间消失,并且最新绘制的元素以相对较高的不透明度出现,直到它们逐渐消失。

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

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

谢谢!

2 个答案:

答案 0 :(得分:1)

它实际上很常见,只是重绘了我在这里找到的东西

WebGL: smoothly fade lines out of canvas

重绘东西意味着你可以保留一些不会淡出的东西。例如,如果您正在制作太空射击游戏并且您只想让爆炸和导弹路径淡出但是您不希望太空飞船和小行星消失,那么您需要通过重绘所有内容并手动完成通过在减少alpha的同时绘制它们来淡出东西

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

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

将fadeColor与第一个帧缓冲区fadeFb1混合/淡入第二个fadeFb2
gl_FragColor = mix(textureColor, fadeColor, mixAmount);

然后您将所有新内容抽取到fadeFb2

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

下一帧你做同样的事情,除了交换你正在绘制的缓冲区以及你正在褪色的缓冲区。

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
...

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

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

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

以下是使用twgl的示例,因为我对于直接的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>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

#Check packages to use in library { library('shiny') #allows for the shiny app to be used library('ggvis') #allows for interactive ploting } alldata <- iris #establish options for drop down menus { specieschoices <- unique(as.character(alldata$Species)) } # UI ui<-fluidPage( titlePanel("Explorer"), fluidRow( column(4, wellPanel( h4("Apply Filters"), selectInput(inputId = "species", label="Select a Species:", choices = sort(specieschoices), selected="setosa", multiple = TRUE, selectize = TRUE) )), column(8, ggvisOutput("plot1") ), column(4, wellPanel( h4("Data Variables"), selectInput(inputId = "x", label="Select x-axis Variable:", choices=as.character(names(alldata[,1:4])),selected='Petal.Length', multiple = FALSE), selectInput(inputId = "y", label="Select y-axis Variable:", choices=as.character(names(alldata[,1:4])),selected='Petal.Width', multiple = FALSE) )), column(4, wellPanel( checkboxInput(inputId = "datarange", label="Specify Data Ranges", value = FALSE), conditionalPanel( condition = "input.datarange == true", wellPanel( numericInput(inputId = "minxdata", label="Specify x axis min", value = -9999999999, step = 0.1), numericInput(inputId = "maxxdata", label="Specify x axis max", value = 9999999999, step = 0.1), numericInput(inputId = "minydata", label="Specify y axis min", value = -9999999999, step = 0.1), numericInput(inputId = "maxydata", label="Specify y axis max", value = 9999999999, step = 0.1) )) )) )) #SERVER server<-function(input,output,session) { #Set up reactive variables for ranges filteredData <- reactive({ minX <- input$minxdata maxX <- input$maxxdata minY <- input$minydata maxY <- input$maxydata # Apply filters m <- alldata %>% filter( `Species` %in% input$species ###############THIS IS THE PART THAT I NEED HELP WITH #This works as hardcoded with hypothetical user input of x=Petal.Length and y=Petal.Width , Petal.Length >= minX, Petal.Length <= maxX, Petal.Width >= minY, Petal.Width <= maxY #This is does not work # , # as.symbol(input$x) >= minX, # as.symbol(input$x) <= maxX, # as.symbol(input$y) >= minY, # as.symbol(input$y) <= maxY ##################################################### ) m <- droplevels(as.data.frame(m)) m }) vis <- reactive({ #Plot Data with Visualization Customization xvar <- prop("x", as.symbol(input$x)) yvar <- prop("y", as.symbol(input$y)) p1 = filteredData() %>% ggvis(x = xvar, y = yvar) %>% layer_points() %>% # Specifies the size of the plot set_options(width = 800, height = 450, duration = 0) }) #Actually plots the data vis %>% bind_shiny("plot1") } #Run the Shiny App to Display Webpage shinyApp(ui=ui, server=server) 标志在内存有限的设备(手机)上很有用,因为它允许这些设备重用那块内存。

淡化/重影效果以不同的方式完成:您分配与视口大小相同的纹理,并在此纹理上进行变暗。每个帧都会将此纹理的内容重新渲染为自身,同时将颜色值与衰落因子(例如0.9)相乘。然后,在相同的纹理上渲染新元素,最后将纹理渲染到视口(一个简单的“复制渲染”)。