将JavaScript变量发送到片段着色器

asi*_*mes 13 javascript glsl webgl

我一直在拼凑在线示例,以制作Mandelbrot Set片段着色器.顶点着色器基本上什么都不做,它分配gl_Position并且片段着色器做一些数学计算图像.

但是,我有一些#define我想用JavaScript控制变量替换的s,我不知道如何做到这一点.如果一个例子可以显示如何#define MAX_ITERATIONS 200用下面的代码中的JavaScript指定变量替换,我可能会弄清楚其余的.我相信我需要指定一个uniform或者varying不确定如何管理从JavaScript到GLSL的通信.

另外我不明白aPositionJavaScript和顶点着色器之间的工作原理,我所拥有的与示例基本相同.

JavaScript,我认为只init()对SO读者有用,其余部分如果需要发布:

var canvas, gl, shaderProgram;

function draw() {
    window.requestAnimationFrame(draw, canvas);

    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}

function init() {
    canvas = document.getElementById("theCanvas");

    gl = initGl(canvas);
    if (!gl) {
        alert("Could not initialize WebGL");
        return;
    }

    shaderProgram = initShaders();
    if (!shaderProgram) {
        alert("Could not initialize shaders");
        return;
    }

    var vertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    gl.bufferData(
        gl.ARRAY_BUFFER,
        new Float32Array([
            -1.0,  -1.0,
            1.0,  -1.0,
            -1.0, 1.0,
            1.0, 1.0,
        ]),
        gl.STATIC_DRAW
    );

    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.viewportWidth = canvas.width;
    gl.viewportHeight = canvas.height;

    var aPosition = gl.getAttribLocation(shaderProgram, "aPosition");
    gl.enableVertexAttribArray(aPosition);
    gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);

    draw();
}

function initGl(inCanvas) {
    gl = false;

    try { gl = inCanvas.getContext("webgl") || inCanvas.getContext("experimental-webgl"); }
    catch (e) {}

    return !gl ? false : gl;
}

function initShaders() {
    var vertexShader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vertexShader, document.getElementById("vertexShader").text);

    gl.compileShader(vertexShader);
    if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
        alert(gl.getShaderInfoLog(vertexShader));
        return false;
    }

    var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fragmentShader, document.getElementById("fragmentShader").text);

    gl.compileShader(fragmentShader);
    if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
        alert(gl.getShaderInfoLog(fragmentShader));
        return false;
    }

    shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
    gl.linkProgram(shaderProgram);

    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) return false;
    gl.useProgram(shaderProgram);

    return shaderProgram;
}
Run Code Online (Sandbox Code Playgroud)

顶点着色器:

attribute vec2 aPosition;

void main() {
    gl_Position = vec4(aPosition, 0.0, 1.0);
}
Run Code Online (Sandbox Code Playgroud)

片段着色器,MAX_ITERATIONS,XMIN,YMIN,并WH应在JavaScript控制:

#ifdef GL_FRAGEMENT_PRECISION_HIGH
    precision highp float;
#else
    precision mediump float;
#endif
precision mediump int;

#define MAX_ITERATIONS 200
#define XMIN -2.5
#define YMIN -2.0
#define WH 4.0

#define LOG_TWO log(2.0)
#define LOG_MAX log(200.0)

void main() {
    // Normalized pixel position to complex plane position
    float maxPwh = max(640.0, 480.0);
    float x = XMIN+(gl_FragCoord.x/maxPwh)*WH;
    float y = YMIN+(gl_FragCoord.y/maxPwh)*WH;

    // Complex plane window offsets for pixel windows that are not square
    float halfDelta = WH/maxPwh*0.5;
    x -= min((640.0-480.0)*halfDelta, 0.0);
    y -= min((480.0-640.0)*halfDelta, 0.0);

    // Mandelbrot Set code
    float zr = x;
    float zi = y;
    int iterations = 0;
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        iterations = i;

        float sqZr = zr*zr;
        float sqZi = zi*zi;
        float twoZri = 2.0*zr*zi;
        zr = sqZr-sqZi+x;
        zi = twoZri+y;

        if (sqZr+sqZi > 16.0) break;
    }

    if (iterations == MAX_ITERATIONS-1) gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    else {
        float fn = float(iterations)+1.0-log(log(sqrt(zr*zr+zi*zi)))/LOG_TWO;
        float logVal = log(fn)/LOG_MAX;

        gl_FragColor = vec4(logVal, logVal, logVal, 1.0);
    }
}
Run Code Online (Sandbox Code Playgroud)

gma*_*man 19

简短的回答是你基本上有两个选择

  1. 通过统一将值从JavaScript传递到GLSL.

    例如,如果要传递浮点,请创建浮点均匀

    uniform float foo;
    
    Run Code Online (Sandbox Code Playgroud)

    在JavaScript中编译并链接该着色器,然后查找制服的位置

    var locationOfFoo = gl.getUniformLocation(someProgram "foo");
    
    Run Code Online (Sandbox Code Playgroud)

    您现在可以将值传递给GLSL

    gl.useProgram(someProgram)
    gl.uniform1f(locationOfFoo, valueToPass);
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在编译着色器之前处理字符串

    #define MAX_INTERATIONS %maxIterations%
    #define XMIN %xMin%
    
    Run Code Online (Sandbox Code Playgroud)

    ...

    var maxIterations = 123;
    var xMin = 4.5;
    shaderSource = shaderSource.replace(/%maxIterations%/g, maxIterations);
    shaderSource = shaderSource.replace(/%xMin%/g, xMin);
    
    Run Code Online (Sandbox Code Playgroud)

(1)以上是为了传递经常变化的东西.#2用于在编译之前更改着色器.#1是几乎100%的WebGL程序中使用的技术.在许多游戏引擎动态生成着色器时经常使用#2.


J.M*_*SON 5

我花了大约 45 分钟来实现gman 的答案,因为\n我不断犯愚蠢的小错误。这是完整的工作代码\n示例,用于创建可调整的图块地图。

\n

测试于: Chrome、Internet Explorer 和 Edge:

\n

\r\n
\r\n
    <!DOCTYPE HTML >\n    <html lang="en">\n    <head>\n    <meta charset="UTF-8">\n    <title>         GL_TILE_TESTBED           </title>\n    <!-- AUTHOR: John Mark Isaac Madison           -->\n    <!-- EMAIL : J4M4I5M7@hotmail.com              -->\n    <!-- SSSSSSSSS SHADER_SECTION START  SSSSSSSSS -->\n    <!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->\n    <!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->\n    <style>\n       \n        p{ font-size:12pt;}\n        h3,p,input,button,br{ \n            padding:0px; \n            margin:0px; \n            font-family:"Andale Mono";\n        }\n        button,input{\n            padding:10px;\n        }\n    </style>\n    <script id="VERT_SHADER" type="NOT_JAVASCRIPT">\n      precision highp float;\n      \n      attribute vec2 a_position;\n      void main() {\n        gl_Position = vec4(a_position, 0, 1);\n      }\n    </script>\n    \n    \n    <script id="FRAG_SHADER" type="NOT_JAVASCRIPT">\n    \n      //Must declare precision before declaring\n      //any uniforms:\n      ////////////////////////////////////////////////\n      #ifdef GL_FRAGMENT_PRECISION_HIGH\n        precision highp float;\n      #else\n        precision mediump float;\n      #endif\n      precision mediump int;\n      ////////////////////////////////////////////////\n    \n      #define CANVAS_WID  640.0\n      #define CANVAS_HIG  480.0\n    \n      #define TIL_WID     64.0\n      #define TIL_HIG     64.0\n      \n      //Uniforms exposed to HTML/JAVASCRIPT:\n      uniform float TIL_WID_EDIT;\n      uniform float TIL_HIG_EDIT;\n     \n      float til_wid;\n      float til_hig;\n      \n    \n      void main() {\n    \n        //If uniforms have not set by user,\n        //use the default values set by the #define(s)\n        //==========================================//\n        if(TIL_WID_EDIT > 0.0){\n          til_wid = TIL_WID_EDIT;\n        }else{\n          til_wid = TIL_WID;\n        }\n        \n        if(TIL_HIG_EDIT > 0.0){\n          til_hig = TIL_HIG_EDIT;\n        }else{\n          til_hig = TIL_HIG;\n        }\n        //==========================================//\n        \n        //NOTE: on "gl_FragCoord" range:\n        //******************************************//\n        //web-gl: In terms of pixel/canvas coords.\n        //OpenGL: In terms of 0 to 1.\n        //******************************************//\n        \n        //:Calculate number of tiles shown on screen:\n        //:This may be fractional:\n        float NUM_TIL_X = CANVAS_WID / til_wid;\n        float NUM_TIL_Y = CANVAS_HIG / til_hig;\n    \n        \n        vec2 FC_MOD;\n        FC_MOD.x = gl_FragCoord.x;\n        FC_MOD.y = gl_FragCoord.y;\n        \n        //You want all tiles to have the full range \n        //of colors, so you always modulate by \n        //CANVAS_WID and CANVAS_HIG, You scale by the \n        //# of tiles on each axis which means the \n        //gradient becomes steeper as the # of tiles\n        //increases.\n        FC_MOD.x = mod( gl_FragCoord.x*NUM_TIL_X, CANVAS_WID );\n        FC_MOD.y = mod( gl_FragCoord.y*NUM_TIL_Y, CANVAS_HIG );\n        \n        //[N]ormalize values into range 0 to 1:\n        //NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN//\n        float norm_X = (FC_MOD.x) / CANVAS_WID;\n        float norm_Y = (FC_MOD.y) / CANVAS_HIG;\n        //NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN//\n        \n        //Use [B]lue channel because why not?\n        //BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB//\n        float GRAD_X = gl_FragCoord.x / CANVAS_WID;\n        //BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB//\n     \n        //Set the final [F]ragment colors:\n        //FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF//\n        gl_FragColor = vec4(norm_X, norm_Y, GRAD_X, 1.0);\n        //FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF//\n      }\n    </script>\n    <!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->\n    <!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->\n    <!-- SSSSSSSSSS SHADER_SECTION END  SSSSSSSSSS -->\n    </head> \n    <!-- HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH -->\n             \n    <!-- BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB -->                    \n    <body onload="ON_LOADED_FUNCTION()" >\n    <!-- BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB -->\n    \n        <h3> Open GL Tile TestBed            <h3>\n        <p> Author: John Mark Isaac Madison <p>\n        <p> Email : J4M4I5M7@hotmail.com    <p>\n    \n        <canvas id="glCanvas"></canvas>\n        \n        </br>\n        <button onClick="PUT_WID();">TILE_WIDTH__IN_PIXELS</button> \n        <input type="text" id="INPUT_WID" value="45">\n        </br>\n        \n        </br>\n        <button onClick="PUT_HIG();">TILE_HEIGHT_IN_PIXELS</button> \n        <input type="text" id="INPUT_HIG" value="45">\n        </br>\n        \n        \n         \n    <!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->   \n    <script id="BOILER_PLATE_CODE">\n        function ON_LOADED_FUNCTION(){\n            console.log("[ON_LOADED_FUNCTION]");\n            main();\n        }\n        \n        //:Takes the gl context object, if the input\n        //:is null, we likely failed to get the\n        //:context.\n        function HAS_OPEN_GL_CHECK(gl){\n          // Only continue if WebGL is \n          // available and working\n          if (!gl) {\n            var msg = "";\n            msg += "[Unable to initialize WebGL.]";\n            msg += "[your browser or machine may]";\n            msg +=  "[not support it.]"\n            alert( msg );\n            return;\n          }\n        }\n        \n        function GET_INPUT_BOX_VALUE( elem_id ){\n            var box; //DOM input box\n            var val; //Value in input box.\n            box = document.getElementById( elem_id );\n            val = box.value;\n            return (0 + val); //cast to number.\n        }\n        \n        function PUT_WID(){\n            assert_program_and_gl_exist();\n            var val = GET_INPUT_BOX_VALUE("INPUT_WID");\n            SET_ATTR("TIL_WID_EDIT", val);\n        }\n        \n        function PUT_HIG(){\n            assert_program_and_gl_exist();\n            var val = GET_INPUT_BOX_VALUE("INPUT_HIG");\n            SET_ATTR("TIL_HIG_EDIT", val); \n        }\n        \n        function SET_ATTR(gl_var_name, val){\n            if(val < 0 || val > 256 ){\n                alert("choose value between 0 to 256");\n                return;\n            }\n         \n            var loc; //<--location of variable.\n            loc = gl.getUniformLocation(\n                program    , \n                gl_var_name\n            );\n            gl.useProgram(program);\n            gl.uniform1f(loc, val);\n        }\n        \n        function assert_program_and_gl_exist(){\n            if(!program){\xe6\x85\x8c("[NO_PROGRAM_EXISTS]");}\n            if(!gl     ){\xe6\x85\x8c("[NO_GL_EXISTS]");}\n        }\n        \n        //\xe6\x85\x8c: "disconcerted, be confused, lose one\'s head"\n        //\xe6\x85\x8c: In Code: ~Panic~\n        function \xe6\x85\x8c( panic_message ){\n            console.log( panic_message );\n            alert      ( panic_message );\n            throw      ( panic_message );\n        }\n      \n        function makeOpenGlContextUsingCanvas(c){\n            \n            //:Try what works in chrome and all the\n            //:respectable browsers first:\n            gl = c.getContext("webgl");\n            \n            if(!gl){\n                console.log("[Probably_In_IE]");\n                gl = c.getContext("experimental-webgl");\n            }else{\n                console.log("[Probably_NOT_IE]");\n            }\n            \n            HAS_OPEN_GL_CHECK( gl );\n            return gl;\n        }\n        \n        //: No "var" prefix, making them global:\n        function initGlobals(){\n            canvas = document.querySelector("#glCanvas");\n            if(!canvas){\n                alert("FAILED_TO_GET_CANVAS");\n            }else{\n                console.log("[GOT_CANVAS]");\n            }\n            \n            gl = makeOpenGlContextUsingCanvas(canvas);\n            \n            \n            //These dimensions are hard-coded into\n            //fragment shader code, so be careful\n            //about changing them:\n            canvas.width = 640;\n            canvas.height= 480;\n        \n            buffer = gl.createBuffer();\n            gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n            gl.bufferData(\n            gl.ARRAY_BUFFER, \n            new Float32Array([\n              -1.0, -1.0, \n               1.0, -1.0, \n              -1.0,  1.0, \n              -1.0,  1.0, \n               1.0, -1.0, \n               1.0,  1.0]), \n            gl.STATIC_DRAW\n            );\n            \n            //G == Global Container.\n            //To fix problems with rendering in I.E.\n            //(Internet Explorer)\n            //GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG//\n            var G = {}; \n            G.canvas = canvas;\n            G.gl     = gl;\n            G.buffer = buffer;\n            \n            if( ! G.canvas ||\n                ! G.gl     ||\n                ! G.buffer  ){\n                \xe6\x85\x8c("[Global_Container_Broken]");\n            }\n            \n            return G;\n            //GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG//\n        }\n        \n        function main(){\n        \n          G = initGlobals();\n          HAS_OPEN_GL_CHECK( G );\n          \n          gl.viewport(0,0,gl.drawingBufferWidth, gl.drawingBufferHeight);\n    \n          setup();\n          render();\n        }\n        \n        function setup(){\n            var frag_dom = document.getElementById("FRAG_SHADER");\n            var frag_src = frag_dom.text;\n            console.log( frag_src );\n    \n            F = createShader(\n                gl,gl.FRAGMENT_SHADER, frag_src\n            );\n    \n            var vert_dom = document.getElementById("VERT_SHADER");\n            var vert_src = vert_dom.text;\n            console.log( vert_src );\n    \n            V = createShader(\n                gl, gl.VERTEX_SHADER, vert_src\n            );\n    \n            //**** MAKE "program" a GLOBAL VAR  ****//\n            program = createProgram(gl,V,F);\n            gl.useProgram( program );\n            \n            if(!program){\n                \xe6\x85\x8c("PROGRAM_IS_NULL");\n            }\n        }\n        \n        function render(){\n          window.requestAnimationFrame(render,canvas);\n        \n          // Set clear color to black, fully opaque\n          gl.clearColor(0.0, 0.0, 0.5, 1.0);\n          \n          // Clear the color buffer with specified clear color\n          gl.clear(gl.COLOR_BUFFER_BIT);\n          \n          //Directly before call to gl.drawArrays:\n          positionLocation = gl.getAttribLocation(program, "a_position");\n          gl.enableVertexAttribArray( positionLocation );\n          gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);\n          \n          \n          gl.drawArrays(gl.TRIANGLES, 0, 6);\n        }\n        \n        function createShader(gl,type,source){\n            //:Error Check For Bad Inputs:\n            if(!gl    ){\xe6\x85\x8c("[NULL_GL]");}\n            if(!type  ){\xe6\x85\x8c("[NULL_TY]");}\n            if(!source){\xe6\x85\x8c("[NULL_SR]");}\n        \n            var shader = gl.createShader(type);\n            gl.shaderSource(shader, source);\n            gl.compileShader(shader);\n            var res = gl.getShaderParameter(shader, gl.COMPILE_STATUS);\n            if( res ){\n                console.log("[SHADER_COMPILED!]");\n                return shader;\n            }\n            \n            console.log(gl.getShaderInfoLog(shader));\n            gl.deleteShader(shader);\n            \xe6\x85\x8c("[FAILED_TO_COMPILE_SHADER]");\n        }\n        \n        //:gl  : openGL context :\n        //:vert: vertex shader  :\n        //:frag: fragment shader:\n        function createProgram(gl,vert, frag){\n            var program = gl.createProgram();\n            gl.attachShader(program, vert);\n            gl.attachShader(program, frag);\n            gl.linkProgram (program);\n            var res = gl.getProgramParameter(program, gl.LINK_STATUS);\n            if( res ){\n                console.log("[PROGRAM_CREATED!]");\n                return program;\n            }\n            \n            console.log(gl.getProgramInfoLog(program));\n            gl.deleteProgram(program);\n        }\n    \n    </script>\n    <!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->\n         \n    <!-- BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB -->\n    </body>\n    </html>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n