如何在WebGL 1.0中将纹理用作屏幕大小的位图?映射到gl_FragCoord的像素坐标?

时间:2018-02-05 22:43:27

标签: textures webgl fragment-shader

这是对早期question的跟进,在那里我问我如何在WebGL 1.0中批处理多个四边形和更改行。我尝试了另一种方法,我想做以下事情:

使用整个窗口大小的纹理。颜色值将充当深度索引。使用gl_FragCoord.xy索引每个片段的纹理。根据我的理解,我不需要任何属性或UV坐标。我只希望0,0是左上角,右下角是(宽度,高度)。 (这可能是一个XY问题,但我仍然希望至少尝试这种方法来获得学习效益。)

我的动机: 以防这是一个XY问题,澄清我希望做的事情:    我需要用线交错四边形层。我希望应用Bresenham算法在纹理上绘制线条,我在其中使用颜色来标记线条在特定像素坐标上的哪个层。这样,我可以分配带有索引的四边形并与烘焙到纹理中的线索引进行比较。如果片段的线索引更大,则应绘制线条颜色。否则四边形位于顶部(也许这会慢得一次做几帧...... Bresenham为每一行写入纹理,将数据重新输入GPU.WebGL 2.0可能有一些额外的深度缓冲有助于此的功能,但我必须使用1.0。)。

问题: ......是我的尝试产生了奇怪的结果。 (注意:我正在使用MDN' s tutorial对视频剪辑中的动态纹理进行参考)。

要查看坐标是否正确,我将颜色设置为4个(RGBA)组中的随机值,其中第4个不透明度始终为255。 我看到的是我的整个形状是一种颜色,这表明纹理实际上是从0到1,每个gl_FragCoord.xy选择一个颜色在一个"像素" (或者我想过一个像素)。这可能是错的,但我目前没有其他解释。到目前为止,搜索没有显示使用纹理作为基本位图的示例。

我会发布一些我的代码片段,以防万一。我正在使用gl-matrix的正交投影矩阵。

纹理创作:

function createScreenDepthTexture(gl, width, height, scale) { // scale is 1 for now
    const texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);
    // I think that I need to flip y since 0, 0 is in the bottom-left corner by default
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);

    const level = 0;
    const internalFormat = gl.RGBA;
    const w = width;
    const h = height;
    const border = 0;
    const srcFormat = gl.RGBA;
    const srcType = gl.UNSIGNED_BYTE;
    const size = width * height * scale * scale * Uint8Array.BYTES_PER_ELEMENT * 4;
    const src =  new Uint8Array(size);

    // from MDN
    function getRandomIntInclusive(min, max) {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min + 1)) + min; //The maximum is inclusive and the minimum is inclusive 
    }

    for (let i = 0; i < size; i += 4) {
        // random colors, but I see only one color in the end
        src[i    ] =   getRandomIntInclusive(0, 255),
        src[i + 1] =   getRandomIntInclusive(0, 255),
        src[i + 2] =   getRandomIntInclusive(0, 255),
        src[i + 3] =   255;
    }

    gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, w * scale, h * scale, border, srcFormat, srcType, src);

    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

    return {texture : texture, src : src, w : width * scale, h : w * scale};
}

纹理设置:

...
const textureRecord = createScreenDepthTexture(G.gl, 640, 480, SCALE);
// G is a wrapper containing the gl context and other data
G.gl.activeTexture(gl.TEXTURE0);
G.gl.bindTexture(gl.TEXTURE_2D, textureRecord.texture);
G.gl.uniform1i(G.gl.getUniformLocation(G.program, "u_sampler"), 0);
...

VERTEX和FRAGMENT SHADERS(此测试中未使用的几个变量,因此我将其排除在外): //注意我的JavaScript投影矩阵是:
mat4.ortho(matProjection, 0.0, G.canvas.width, G.canvas.height, 0.0, -1000.0, 1000.0);

// VERTEX
precision highp float;

attribute vec3 a_position;
uniform mat4 u_matrix;

void main() {
   v_color = u_color;
   gl_Position = u_matrix * vec4(a_position, 1.0);
}

// FRAGMENT
precision highp float;

uniform sampler2D u_sampler;

void main() {
   // I thought that this would set the color of this specific pixel
   // to be the color of the texture at this specific coordinate,
   // but I think that the coordinate systems are off if I see only one color,
   // is the sampler set to 0-1? How would I change this correctly?
   gl_FragColor = texture2D(u_sampler, gl_FragCoord.xy);
}

我还有什么可能遗失的?此外,请随时告诉我,这个实验实际上是否因为性能问题而无法实现。我重复地连续几行重写纹理。提前谢谢。

1 个答案:

答案 0 :(得分:1)

   // I thought that this would set the color of this specific pixel
   // to be the color of the texture at this specific coordinate,
   // but I think that the coordinate systems are off if I see only one color,
   // is the sampler set to 0-1? How would I change this correctly?
   gl_FragColor = texture2D(u_sampler, gl_FragCoord.xy);

texture2D采用标准化纹理坐标,无法直接访问WebGL 1中纹素空间中的纹理(在WebGL 2中你使用texelFetch),因此需要计算标准化纹理坐标( UVs)将gl_FragCoord除以纹理的宽度和高度(在本例中为屏幕/窗口)。

回答评论中的问题:

  

可能存在舍入错误

并非真的因为所有数学都是32位浮点数,它具有足够的精度,你可能需要确保通过将标准化坐标偏移半个纹理像素来对纹素的中心进行采样。

  

如果我要转移到WebGL 2,那会怎么做呢?

使用texelFetch

texture2d intead
  

对这种方法效率的任何想法?

好吧,我认为它会很慢,如果我理解正确,流程会像:

10 draw quad with shader reading from your screen texture
20 read back the result
30 update the texture
40 goto 10

如果是这样的话,那就是性能的纯毒...... 所以是的,我会尝试使线条工作,或者在其上绘制带有线条像素着色器的条带(使用硬件z缓冲区,像素着色器来建立漂亮,恒定宽度的线条)。