2016-07-04 48 views
2

我想从片段着色器中的某个变量中保存一个计算值,这样我就可以在下次使用它了。如何在片段着色器中保存值以便稍后使用它?

目前,我使用的是巨大的算法准备图像,我想将它保存到一些vec4,并再次要求,我只想拿到vec4,应该说

gl_FragColor = vec4(previously saved variable)

这问题与另一个question here有关,这也是我问的,但我觉得如果这个问题有答案,那么我可以很容易地破解另一个问题。

有什么建议吗?

回答

3

WebGL中的片段着色器写入2件事中的1件。 (1)画布到(2)帧缓冲区的附件。帧缓冲的附件可以是纹理。纹理可以用作着色器的输入。因此,您可以写入纹理并在下一次绘制中使用该纹理。

下面是一个例子

var vs = ` 
 
attribute vec4 position; 
 
void main() { 
 
    gl_Position = position; 
 
} 
 
`; 
 
var fs = ` 
 
precision mediump float; 
 
uniform sampler2D u_texture; 
 
void main() { 
 
    // just grab the middle pixel(s) from the texture 
 
    // but swizzle the colors g->r, b->g, r->b 
 
    gl_FragColor = texture2D(u_texture, vec2(.5)).gbra; 
 
}`; 
 

 
var canvas = document.querySelector("canvas"); 
 
var gl = canvas.getContext("webgl"); 
 
var program = twgl.createProgramFromSources(gl, [vs, fs]); 
 

 
var positionLocation = gl.getAttribLocation(program, "position"); 
 
// we don't need to look up the texture's uniform location because 
 
// we're only using 1 texture. Since the uniforms default to 0 
 
// it will use texture 0. 
 

 
// put in a clipspace quad 
 
var buffer = gl.createBuffer(); 
 
gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 
 
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 
 
    -1, -1, 
 
    1, -1, 
 
    -1, 1, 
 
    -1, 1, 
 
    1, -1, 
 
    1, 1, 
 
]), gl.STATIC_DRAW); 
 
    
 

 
gl.enableVertexAttribArray(positionLocation); 
 
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); 
 

 
// make 2 1x1 pixel textures and put a red pixel the first one 
 
var tex1 = gl.createTexture(); 
 
gl.bindTexture(gl.TEXTURE_2D, tex1); 
 
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, 
 
       gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255])); 
 
var tex2 = gl.createTexture(); 
 
gl.bindTexture(gl.TEXTURE_2D, tex2); 
 
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, 
 
       gl.UNSIGNED_BYTE, null); 
 

 
// make a framebuffer for tex1 
 
var fb1 = gl.createFramebuffer(); 
 
gl.bindFramebuffer(gl.FRAMEBUFFER, fb1); 
 
// attach tex1 
 
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, 
 
         gl.TEXTURE_2D, tex1, 0); 
 
// check this will actually work 
 
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== 
 
    gl.FRAMEBUFFER_COMPLETE) { 
 
    alert("this combination of attachments not supported"); 
 
} 
 

 
// make a framebuffer for tex2 
 
var fb2 = gl.createFramebuffer(); 
 
gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); 
 
// attach tex2 
 
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, 
 
         gl.TEXTURE_2D, tex2, 0); 
 
// check this will actually work 
 
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== 
 
    gl.FRAMEBUFFER_COMPLETE) { 
 
    alert("this combination of attachments not supported"); 
 
} 
 

 
function render() { 
 
    gl.useProgram(program); 
 
    // render tex1 to the tex2 
 
    
 
    // input to fragment shader 
 
    gl.bindTexture(gl.TEXTURE_2D, tex1); 
 
    
 
    // output from fragment shader 
 
    gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); 
 
    gl.viewport(0, 0, 1, 1); 
 
    gl.drawArrays(gl.TRIANGLES, 0, 6); 
 
    
 
    // render to canvas so we can see it 
 
    gl.bindFramebuffer(gl.FRAMEBUFFER, null); 
 
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); 
 
    // input to fragment shader, the texture we just rendered to 
 
    gl.bindTexture(gl.TEXTURE_2D, tex2); 
 
    gl.drawArrays(gl.TRIANGLES, 0, 6); 
 
    
 
    // swap which texture we are rendering from and to 
 
    var t = tex1; 
 
    tex1 = tex2; 
 
    tex2 = t; 
 
    
 
    var f = fb1; 
 
    fb1 = fb2; 
 
    fb2 = f; 
 
    
 
    requestAnimationFrame(render); 
 
} 
 
requestAnimationFrame(render);
<script src="https://twgljs.org/dist/twgl-full.min.js"></script> 
 
<canvas></canvas>

的样品中的纹理上面提出红色。然后通过调整颜色来渲染纹理。绿色进入红色通道,蓝色进入绿色通道,红色进入蓝色通道。

我做了2个纹理并将它们附加到2个帧缓冲区。

第一迭代

tex1 = red 
tex2 = 0,0,0,0 
render to fb2 
tex2 is now blue (because red was copied to blue) 
render tex2 to canvas (canvas is now green because blue is copied to green) 
switch which textures we're rendering to 

第二迭代

tex1 = blue (was tex2 last time) 
tex2 = red (was tex1 last time) 
render to fb2 (was fb1 last time) 
tex2 = green (because blue is copied to green) 
render tex2 to canvas (canvas is now red because green is copied to red) 
switch which textures we're rendering to 
+0

我已经理解你的例子上面的帧缓冲区。非常感谢。然而,第二次迭代'tex2 = green(因为蓝色复制为绿色)'这一行很混乱,因为如果我们交换帧缓冲区,我们如何期望片段着色器的输出,即'gl.bindFramebuffer(gl.FRAMEBUFFER,fb2) ; '直接分配给tex2? –

+0

此外,有没有什么办法通过使用WebGLTexture接口知道每个纹理的颜色值(RGBA),这将帮助我更好地理解和调试事物,现在我只是猜测事情:( –

+0

'fb2'有使用'gl.framebufferTexture2D'附加'tex2'。'texture2D(u_texture,...)'从'tex1'读取,'gl_FragColor = ..'正在写入tex2,因为它是'COLOR_ATTACHMENT0' framebuffer'fb2' 至于你的第二个问题,如果你绑定'fb2',那么'gl.readPixels'会从'te​​x2'返回像素,如果你绑定'fb1',那么'gl.readPixels'将会返回来自'tex1'的像素。 。如果你将'null'绑定为当前framebuffer,那么'gl.readPixels'将返回画布中的像素(除了WebGL根据设置清除画布的问题外)。 – gman

1

片段着色器按片段(像素)执行。与其他任何着色器一样,默认情况下它不能存储值,正如您在常规编程语言中所期望的那样。 有几种方法可以做你想做的事: 你可以使用imageLoad/Store,它允许你读取和写入着色器中的数据到图像中。 Image使用GL纹理作为内存存储。它的好处在于,您可以在使用图像时保存和加载数值数据而不会失去精度,因为在通过图像访问纹理数据时禁用纹理过滤。

在着色器中存储和读取数据的另一种方式是使用buffers.Uniform缓冲区,或自GL4.3 Shader storage buffers.。 SSBO允许读取和写入大量数据。真正由您决定哪些用于在着色器中存储和检索数据。有些人认为某些硬件上的纹理内存访问速度更快。从我的经验,使用SSBO vs图像加载存储,我没有发现Nvidia GPU上的性能有显着差异。

在您的场景中,我可能会使用图像加载/存储。因为您可以像使用相同的UV索引一样将图像数据用于采样纹理。另外,我不太清楚你使用的是什么版本的OpenGL,但是要使用这些扩展,你必须使用GL4.2和GL4.3。

+0

我使用的WebGL 1.0,它遵循的OpenGL ES 2.0,我想。我拥有GL4的所有这些奢侈品吗?2和GL4.3? –

+0

不幸的是,你没有。 –

+0

好的,那我该如何坚持vec4,以便以后可以访问它,任何想法? –

相关问题