Glsl WebGL:具有动态数据的2D片段着色器

Glsl WebGL:具有动态数据的2D片段着色器,glsl,webgl,fragment-shader,Glsl,Webgl,Fragment Shader,我正在开发一款自上而下的2D HTML5游戏,它使用逐像素区域采样和操作来创建波浪涟漪效果。我在JavaScript中安装并运行了它,但在FireFox上性能不稳定,在Chrome上完全无法接受。我考虑将我的整个原型移植到一个性能更好的平台上,但在此过程中学习了GL着色器 我认为将我的算法调整为GL片段着色器足够简单。我已经连续第四天试图让我的着色器产生任何输出。我已经尽了最大努力使其他问题和教程中的解决方案适应我所做的工作,但没有一个与我的具体需求足够接近 首先,我将从概念上概述我需要做的事情

我正在开发一款自上而下的2D HTML5游戏,它使用逐像素区域采样和操作来创建波浪涟漪效果。我在JavaScript中安装并运行了它,但在FireFox上性能不稳定,在Chrome上完全无法接受。我考虑将我的整个原型移植到一个性能更好的平台上,但在此过程中学习了GL着色器

我认为将我的算法调整为GL片段着色器足够简单。我已经连续第四天试图让我的着色器产生任何输出。我已经尽了最大努力使其他问题和教程中的解决方案适应我所做的工作,但没有一个与我的具体需求足够接近

首先,我将从概念上概述我需要做的事情。然后,我将提供代码并解释到目前为止我尝试采用的方法。如果我能理解我需要做什么,我愿意从头开始

波浪效应的算法如所述工作。它涉及通过基于每个像素的波高数据替换某些像素来从源图像渲染新图像,每个像素存储在两个矩阵中,每个像素对应一个条目。一个用作水的当前状态,另一个存储前一帧的结果以用于计算当前

每帧:通过平均waveMapPrevious值计算waveMapCurrent

每像素:位移是从waveMapCurrent中的高度计算的,并且(在psuedocode中)
newPixelData[current]=sourcePixelData[current+displacement]

至少,我需要我的片段着色器能够访问当前波高矩阵中的数据和用作源的图像。如果我理解正确,将新数据传递到GL管道的次数降至最低,并在着色器中执行波高计算,对性能最为有利,但我也可以在脚本中进行计算,并在每一帧将更新版本的波高矩阵传递给片段着色器

然而,在我思考片段着色器正在做什么之前,有一项任务是设置一个对象来实际绘制片段。据我所知,这需要设置顶点来表示画布,并将它们设置到画布的各个角落,以使WebGL将其渲染为平面2D图像,但这似乎不直观。我需要将其渲染到一个图像以用作背景纹理,或者初始化第二个画布并将第一个画布的背景设置为透明(这是我在下面的代码中尝试的)。如果有任何方法可以让片段着色器运行,并简单地使用与画布/图像像素1:1对应的每个片段渲染其输出,那将是崇高的,但我假设没有使用GLSL

我试图做的是将当前波高矩阵打包为纹理,并将其作为统一的二维采样器发送进来。在当前状态下,我的代码运行,但WebGL告诉我,活动纹理1(作为纹理打包的波高矩阵)不完整,并且它的缩小/放大过滤未设置为“最近”,即使我已尝试显式将其设置为“最近”。我不知道如何进一步调试它,因为WebGL引用我对gl.DrainElements的调用作为错误源

这是我所能描述的最聪明的。以下是我所拥有的:

    ws.glProgram = function(gl, tex) {

    var flExt = gl.getExtension("OES_texture_float");
    ws.program = gl.createProgram();
    var vertShader = gl.createShader(gl.VERTEX_SHADER);
    var fragShader = gl.createShader(gl.FRAGMENT_SHADER);

    var vertSrc = [

    "attribute vec4 position;",

    "void main(void) {",
        "gl_Position = position;",
    "}"

    ]

    var fragSrc = [
    "precision highp float;",

    "uniform sampler2D canvasTex;",
    "uniform sampler2D dataTex;",

    "uniform vec2 mapSize;",
    "uniform float dispFactor;",
    "uniform float lumFactor;",

    "void main(void) {",


        "vec2 mapCoord = vec2(gl_FragCoord.x+1.5, gl_FragCoord.y+1.5);",
        "float wave = texture2D(dataTex, mapCoord).r;",
        "float displace = wave*dispFactor;",

        "if (displace < 0.0) {",
            "displace = displace+1.0;",
        "}",

        "vec2 srcCoord = vec2(gl_FragCoord.x+displace,gl_FragCoord.y+displace);",

        "if (srcCoord.x < 0.0) {",
            "srcCoord.x = 0.0;",
        "}",
        "else if (srcCoord.x > mapSize.x-2.0) {",
            "srcCoord.x = mapSize.x-2.0;",
        "}",

        "if (srcCoord.y < 0.0) {",
            "srcCoord.y = 0.0;",
        "}",
        "else if (srcCoord.y > mapSize.y-2.0) {",
            "srcCoord.y = mapSize.y-2.0;",
        "}",

        "float lum = wave*lumFactor;",
        "if (lum > 40.0) { lum = 40.0; }",
        "else if (lum < -40.0) { lum = -40.0; }",

        "gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);", // Fragment Shader is not producing output

        /*"gl_FragColor = texture2D(canvasTex, srcCoord);",
        "gl_FragColor.r = gl_FragColor.r + lum;",
        "gl_FragColor.g = gl_FragColor.g + lum;",
        "gl_FragColor.b = gl_FragColor.b + lum;",*/

    "}"];

    vertSrc = vertSrc.join('\n');
    fragSrc = fragSrc.join('\n');

    gl.shaderSource(vertShader, vertSrc);
    gl.compileShader(vertShader);
    gl.attachShader(ws.program, vertShader);

    gl.shaderSource(fragShader, fragSrc);
    gl.compileShader(fragShader);
    gl.attachShader(ws.program, fragShader);

    console.log(gl.getShaderInfoLog(vertShader));

    gl.linkProgram(ws.program);
    gl.useProgram(ws.program);

    // Vertex Data for rendering surface
    var vertices = [ 0,0,0, 1,0,0,
                     0,1,0, 1,1,0 ];
    var indices = [  0,1,2, 0,2,3 ];

    ws.program.vertices = new Float32Array(vertices);
    ws.program.indices = new Float32Array(indices);

    gl.enableVertexAttribArray(0);
    var vBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, ws.program.vertices, gl.STATIC_DRAW);
    gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);

    var iBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, ws.program.indices, gl.STATIC_DRAW);

    // Send texture data from tex to WebGL
    var canvasTex = gl.createTexture();
    gl.activeTexture(gl.TEXTURE2);
    gl.bindTexture(gl.TEXTURE_2D, canvasTex);

    // Non-Power-of-Two Texture Dimensions
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    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.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, tex.imageData);
    gl.uniform1i(gl.getUniformLocation(ws.program, "canvasTex"), 2);

    // Send empty wave map to WebGL
    ws.activeWaveMap = new Float32Array((ws.width+2)*(ws.height+2));
    ws.dataPointerGL = gl.createTexture();
    gl.activeTexture(gl.TEXTURE1);
    gl.bindTexture(gl.TEXTURE_2D, ws.dataPointerGL);

    // Non-Power-of-Two Texture Dimensions
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    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.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, ws.width+2,ws.height+2,0, gl.LUMINANCE, gl.FLOAT, ws.activeWaveMap);
    gl.uniform1i(gl.getUniformLocation(ws.program, "dataTex"), 1);

    // Numeric Uniforms
    gl.uniform2f(gl.getUniformLocation(ws.program, "mapSize"), ws.width+2,ws.height+2);
    gl.uniform1f(gl.getUniformLocation(ws.program, "dispFactor"), ws.dispFactor);
    gl.uniform1f(gl.getUniformLocation(ws.program, "lumFactor"), ws.lumFactor);

    return ws.program;

}

    ws.render = function(gl, moves, canvas) {
    //canvas.clear();
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // specify gl.clearColor?

    for (g=0, fl=0; g < ws.tempWaveMap.length; g++) {
        for (b=0; b < ws.tempWaveMap[g].length; b++) {
            ws.tempWaveMap[g][b] = ws.activeWaveMap[fl];
            fl += 1;
        }
    }

    for (j=0; j < moves.length; j++) {
        ws.setWave(moves[j],ws.tempWaveMap);
    }

    for (x=1; x <= ws.width; x++) {
        for (y=1; y <= ws.height; y++) {
            ws.resolveWaves(ws.inactiveWaveMap, ws.tempWaveMap, x,y);
        }
    }

    for (g=0, fl=0; g < ws.inactiveWaveMap.length; g++) {
        for (b=0; b < ws.inactiveWaveMap[g].length; b++) {
            ws.outgoingWaveMap[fl] = ws.inactiveWaveMap[g][b];
            ws.inactiveWaveMap[g][b] = ws.tempWaveMap[g][b];
            fl += 1;
        }
    }

    ws.activeWaveMap.set(ws.outgoingWaveMap);

    gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, ws.width+2,ws.height+2,0, gl.LUMINANCE, gl.FLOAT, ws.activeWaveMap);

    gl.drawElements(gl.TRIANGLES, ws.program.indices.length, gl.UNSIGNED_BYTE, 0);
}

您无法从着色器读取WebGL中画布的内容,因此需要创建纹理并将该纹理附加到帧缓冲区(帧缓冲区只是附件的集合)。这样可以渲染到纹理,并在其他渲染中使用结果

接下来,这段代码没有多大意义

  vec2 mapCoord = vec2(gl_FragCoord.x+1.5, gl_FragCoord.y+1.5);
  float wave = texture2D(dataTex, mapCoord).r;
<> >代码> GLYFRACORORD 不在纹理坐标中,它在绝对目标像素中,如果你在屏幕中间的矩形<代码> GLY-FraceOrd将有最大像素坐标(不在0开始)。对于+1.5,纹理坐标中任何方向上的1.5都是纹理宽度或高度的1.5倍

如果你想向左或向右看一个像素,你需要知道纹理的大小

纹理坐标中的一个像素单位是横向的
1/宽度
,向下的
1/高度
。换句话说,如果你有一些纹理坐标参照一个像素

 vec2 texcoord = vec2(0.5, 0.5);  // center of texture
你想让右边有一个像素

 vec2 onePixelRight = texcoord + vec2(1.0 / textureWidth, 0);
WebGL1中没有传递
textureWidth
的宽度,因此您必须制作一件制服并将其传递给自己

你可以看到

查看你发布的链接,你需要4个纹理

  • 1纹理是你的图像(我想你想置换这个?)
  • 1纹理是你当前的波浪
  • 1纹理是你的前一波
  • 1纹理是你的下一波
您需要3个帧缓冲区。每波一个

  • 每帧

    • 绑定使用下一个wave的帧缓冲区,以便渲染到下一个wave
    • 使用从上一个波计算当前波的着色器进行渲染
    • 为帧缓冲区绑定null,以便渲染到画布
    • 使用使用当前波浪纹理作为图像纹理置换的着色器进行渲染
    • 将newWave换成current,current换成prev,prev换成next

      注:(这只意味着更改代码中的变量,不需要移动数据)

下面是一些基于原始版本的代码。因为我不能运行原版,所以我不知道它应该是什么样子

函数main(){
变量宽度=256;
变量高度=256;
var gl=document.querySelector(“canvas”).getContext(“webgl”);
var flex=gl.getExtension(“OES_纹理_浮动”);
//除非您想让用户感到困惑,否则您应该始终检查这一点
如果(!flex){
警报(“上没有可用的浮点纹理”
 vec2 onePixelRight = texcoord + vec2(1.0 / textureWidth, 0);