在WebGL中切换着色器

3

我有一个关于WebGL程序的问题,但是我不知道如何调试它,因为浏览器控制台没有显示任何错误。WebGL根本没有绘制任何内容。

我有以下一组着色器:

<script id="shader-fs" type="x-shader/x-fragment">
precision mediump float;

uniform sampler2D uSampler;

varying vec2 vTextureCoord;    
varying vec3 vEye;
varying vec3 vNormal;
uniform vec3 uLightDirection;    // Vector direccion de la luz
uniform vec3 uDirectionalColor; // Color de la luz direcional

uniform vec3 uColShadeless;
uniform vec3 uAmbientColor;
uniform float uKAmbiente;
uniform vec3 uColDifuso;
uniform float uKDifuso;
uniform vec3 uColEspecular;
uniform float uKEspecular;
uniform float uGlossiness;

void main(void) 
{
    vec3 normal = normalize(vNormal);
    float mLambert = max(dot(normal, uLightDirection), 0.0);
    vec3 vLuzLambert = uDirectionalColor * mLambert;

    vec3 r = 2.0 * max(dot(normal, uLightDirection), 0.0) * normal - uLightDirection;
    //vec3 r = reflect(uLightDirection, normal); // <- Da glossines del otro lado también
    float specular = pow(max(dot(r, normalize(vEye)), 0.0), uGlossiness) ;
    vec4 textureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
    vec3 componenteShadeless = uColShadeless * textureColor.rgb * uColDifuso;             // luz autoiluminada * colores difusos
    vec3 componenteAmbiente = uKAmbiente * uAmbientColor * textureColor.rgb * uColDifuso; // k% * luz ambiente * colores difusos
    vec3 componenteDifusa = uKDifuso * textureColor.rgb * uColDifuso * vLuzLambert;
    vec3 componenteEspecular = uKEspecular * specular * uColEspecular ;
    gl_FragColor = vec4(componenteShadeless + componenteAmbiente + componenteDifusa + componenteEspecular, textureColor.a);
}
</script>

<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec3 aVertexNormal;
attribute vec2 aTextureCoord;

uniform mat4 uViewMatrix;
uniform mat4 uModelMatrix;
uniform mat4 uPMatrix;
uniform mat3 uNMatrix;

varying vec2 vTextureCoord;
varying vec3 vEye;
varying vec3 vNormal;

uniform vec2 aUVOffset;

void main(void) 
{

        // Transformamos al vértice al espacio de la cámara
        vec4 pos_camera_view = uViewMatrix * uModelMatrix * vec4(aVertexPosition, 1.0);
        // Transformamos al vértice al espacio de la proyección
        gl_Position = uPMatrix * pos_camera_view;
        // Coordenada de textura
        vTextureCoord.x = aTextureCoord.x + aUVOffset.x;
        vTextureCoord.y = aTextureCoord.y + aUVOffset.y;

        // Para iluminación
        vEye = -vec3(pos_camera_view.xyz);
        vNormal = uNMatrix * aVertexNormal;
}
</script>

<script id="vs" type="x-shader/x-vertex">
        attribute vec3 aPositionL;
        attribute vec3 aNormalL;
        attribute vec3 aTangentL;
        attribute vec2 aTexCoord;

        uniform mat4 uMatrixMVP;
        uniform mat4 uMatrixMV;

        varying vec4 vPositionV;
        varying vec3 vNormalV;
        varying vec3 vTangentV;
        varying vec2 vTexCoord;

        attribute vec3 aVertexPosition;
        attribute vec3 aVertexNormal;
        attribute vec2 aTextureCoord;

        uniform mat4 uViewMatrix;
        uniform mat4 uModelMatrix;
        uniform mat4 uPMatrix;
        uniform mat3 uNMatrix;
        varying vec3 vNormal;

        uniform vec2 aUVOffset;
        void main(void) 
        {

        // Transformamos al vértice al espacio de la cámara
        vec4 pos_camera_view = uViewMatrix * uModelMatrix * vec4(aPositionL, 1.0);
        // Transformamos al vértice al espacio de la proyección
        gl_Position = uPMatrix * pos_camera_view;

        vNormal = uNMatrix * aVertexNormal;
        vPositionV = uMatrixMV * vec4(aPositionL, 1.0);
        vNormalV = (uMatrixMV * vec4(aNormalL, 0.0)).xyz;
        vTangentV = (uMatrixMV * vec4(aTangentL, 0.0)).xyz;
        vTexCoord = aTexCoord;
}
</script>

<script id="fs" type="x-shader/x-fragment">
        #ifdef GL_ES
        precision highp float;
        #endif

        uniform sampler2D uColorSampler;
        uniform sampler2D uNormalSampler;
        uniform float uTime;

        varying vec4 vPositionV;
        varying vec3 vNormalV;
        varying vec3 vTangentV;
        varying vec2 vTexCoord;

        void main(void) {
            vec3 diffuse = texture2D(uColorSampler, vTexCoord).rgb;
            vec3 normalT = texture2D(uNormalSampler, vTexCoord).xyz;
            normalT.y = 1.0 - normalT.y;
            normalT = 2.0 * normalT - vec3(1.0, 1.0, 1.0);
            normalT.z *= 10.0;

            vec3 binormalV = cross(vNormalV, vTangentV);
            vec3 normalV = normalT.x * vTangentV + normalT.y * binormalV + normalT.z * vNormalV;

            normalV = normalize(normalV);
            vec3 lightV = normalize(vec3(10.0 * cos(uTime), 10.0, 10.0 * sin(uTime)));

            float d = dot(normalV, lightV);
            float s = dot(reflect(-lightV, normalV), normalize(-vPositionV.xyz));
            s = pow(s, 30.0);

            vec3 color = diffuse * (0.1 + 0.5 * d + 0.4 * s);

            gl_FragColor = vec4(color, 1.0);
        }

So和and fs是用于绘制带有法线贴图纹理的表面的着色器。我在其余的代码中使用shader-fs和shader-vs。

以下代码用于初始化着色器并更改它们:

function initShaders()
{
    var fragmentShader = getShader(gl, "shader-fs");
    var vertexShader = getShader(gl, "shader-vs");

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

    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
        alert("Could not initialise shaders");
    }

    initShaders2();
}

function changeShaderBasic()
{

    gl.useProgram(shaderProgramMap);

    gl.disableVertexAttribArray(shaderProgramMap.aPositionL);
    gl.disableVertexAttribArray(shaderProgramMap.aNormalL);
    gl.disableVertexAttribArray(shaderProgramMap.aTangentL);
    gl.disableVertexAttribArray(shaderProgramMap.aTexCoord);

    gl.useProgram(shaderProgram);

    shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
    gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

    shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
    gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute);

    shaderProgram.vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal");
gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute);

    shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
    shaderProgram.ViewMatrixUniform = gl.getUniformLocation(shaderProgram, "uViewMatrix");
    shaderProgram.ModelMatrixUniform = gl.getUniformLocation(shaderProgram, "uModelMatrix");
    shaderProgram.nMatrixUniform = gl.getUniformLocation(shaderProgram, "uNMatrix");
    shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");

    shaderProgram.ambientColorUniform = gl.getUniformLocation(shaderProgram, "uAmbientColor"); // Color ambiente
    shaderProgram.lightingDirectionUniform = gl.getUniformLocation(shaderProgram, "uLightDirection"); // Direccion de la luz
    shaderProgram.directionalColorUniform = gl.getUniformLocation(shaderProgram, "uDirectionalColor"); // Color de la luz

    shaderProgram.shadelessColorUniform = gl.getUniformLocation(shaderProgram, "uColShadeless");
    shaderProgram.ambientKUniform = gl.getUniformLocation(shaderProgram, "uKAmbiente");
    shaderProgram.diffuseColorUniform = gl.getUniformLocation(shaderProgram, "uColDifuso");
    shaderProgram.diffuseKUniform = gl.getUniformLocation(shaderProgram, "uKDifuso");
    shaderProgram.specularColorUniform = gl.getUniformLocation(shaderProgram, "uColEspecular");
    shaderProgram.specularKUniform = gl.getUniformLocation(shaderProgram, "uKEspecular");
    shaderProgram.specularGlossiness = gl.getUniformLocation(shaderProgram, "uGlossiness");    

    shaderProgram.uvOffsetUniform = gl.getUniformLocation(shaderProgram, "aUVOffset");

}



function initShaders2() {

            var vs = getShader(gl,'vs');
            var fs = getShader(gl,'fs');

            shaderProgramMap = gl.createProgram();
            gl.attachShader(shaderProgramMap, vs);
            gl.attachShader(shaderProgramMap, fs);
            gl.linkProgram(shaderProgramMap);

            if (!gl.getProgramParameter(shaderProgramMap, gl.LINK_STATUS)) {
                alert('Could not link the shader normal program.');
                return;
            }
}


function changeShaderNormal()
{
    gl.useProgram(shaderProgram);
    gl.disableVertexAttribArray(shaderProgram.vertexPositionAttribute);
    gl.disableVertexAttribArray(shaderProgram.textureCoordAttribute);
    gl.disableVertexAttribArray(shaderProgram.vertexNormalAttribute);

    gl.useProgram(shaderProgramMap);

    shaderProgramMap.ViewMatrixUniform = gl.getUniformLocation(shaderProgramMap, "uViewMatrix");
    shaderProgramMap.ModelMatrixUniform = gl.getUniformLocation(shaderProgramMap, "uModelMatrix");
    shaderProgramMap.aPositionL = gl.getAttribLocation(shaderProgramMap, 'aPositionL');
    gl.enableVertexAttribArray(shaderProgramMap.aPositionL);
    shaderProgramMap.aNormalL = gl.getAttribLocation(shaderProgramMap, 'aNormalL');
    gl.enableVertexAttribArray(shaderProgramMap.aNormalL);
    shaderProgramMap.aTangentL = gl.getAttribLocation(shaderProgramMap, 'aTangentL');
    gl.enableVertexAttribArray(shaderProgramMap.aTangentL);
    shaderProgramMap.aTexCoord = gl.getAttribLocation(shaderProgramMap, 'aTexCoord');
    gl.enableVertexAttribArray(shaderProgramMap.aTexCoord);
    shaderProgramMap.uMatrixMVP = gl.getUniformLocation(shaderProgramMap, 'uMatrixMVP');
    shaderProgramMap.uMatrixMV = gl.getUniformLocation(shaderProgramMap, 'uMatrixMV');
    shaderProgramMap.uColorSampler = gl.getUniformLocation(shaderProgramMap, 'uColorSampler');
    shaderProgramMap.uNormalSampler = gl.getUniformLocation(shaderProgramMap, 'uNormalSampler');
    shaderProgramMap.uTime = gl.getUniformLocation(shaderProgramMap, 'uTime');
}

首先我调用函数“changeShaderBasic”,当我想要用法线贴图绘制一个表面时,我会这样做:

changeShaderNormal();
*Draw the surface*
changeShaderBasic();

如果我删除代码的那一部分,它就可以正常工作-因此着色器shader-fs和shader-vs似乎工作正常。问题似乎在于其他着色器(vs和fs)或切换着色器的函数。
我省略了程序用来创建纹理的代码部分。
我不知道我做错了什么,也不知道如何找到问题。

你应该真正检查 getAttribLocation 的返回值是否为 -1,以及 getUniformLocation 的返回值是否为 null,以防它们没有被找到。 - Kenney
我刚刚检查了所有的内容,问题仍然存在。感谢您的建议。 - Kuranes
我看到你在 disableVertexAttribArray( shaderProgram.vertexPositionAttribute ),但是在 shaderProgramMap.vertexPositionAttribute 上没有启用它;你也没有初始化它。纹理坐标和顶点法线也是一样的情况。 - Kenney
我以前从未尝试过这个(切换着色器),而且我不明白你的意思。shaderProgramMap没有那个属性(vertexPositionAttribute)。两个着色器应该具有相同的属性吗? - Kuranes
按下Control-F,输入“VertexPosition”并搜索。您会发现在“shader-vs”和“vs”中都定义了“aVertexPosition”,因此是的,您还需要在“shaderProgramMap”(即“vs”和“fs”着色器)中初始化该属性(以及其他属性)。我自己也遇到了这个问题,因为代码不再清晰,所以我编写了一个函数,在着色器源代码上映射正则表达式,提取属性和统一变量(以及它们的类型),以便我可以进行运行时检查,看看是否设置了所有参数(以及是否请求了不存在的参数)。 - Kenney
2
你可能会发现这个链接有用:http://webglfundamentals.org/webgl/lessons/webgl-drawing-multiple-things.html - gman
1个回答

3
问题在于一些着色器参数没有被启用,例如 shaderProgramMap 中的一些参数。
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

并且

gl.disableVertexAttribArray(shaderProgram.vertexPositionAttribute);

但是没有针对shaderProgramMap的任何内容,这个脚本也有aVertexPosition属性。

当你开始遇到这种问题时,即使是你自己写的代码,你也找不出错在哪里,那是因为代码不再清晰:概述已经丢失。存在重复的代码:同样的事情被做了两次,即编译、链接和配置Shader。然后,就是重构的时候了:提取所有重复的代码,并将不同的部分参数化。

有许多可能的步骤。其中之一是抽象着色器参数:attributesuniforms。
下面的代码将生成一个参数哈希映射,其中包含有关着色器源中所有参数的信息。请注意,它可能并没有涵盖所有可能性,只是为了给你一个基本想法。

function initShaderParams( gl, shaderProgram, vs, fs )
{
    /** We'll be returning this: */
    var params = {
        uniform:{},
        attribute:{},

        uniforms: function(arr) {
            for ( var a in arr )
                if ( params.uniform[a] )
                    params.uniform[ a ].set( arr[a] );
                else throw new Error("unknown uniform '"+a+"' referenced in shader " + vs+"+"+fs
                    + ";\navailable uniforms: " + Object.keys(params.uniform)
                );
        },

        enable: function() {
            for ( var a in this.attribute )
                gl.disableVertexAttribArray( this.attribute[a] );
        }

        // disable: ....
    };


    /** returns a function to set the value of a uniform given it's type */
    function getUniformFN( type ) {
        switch ( type ) {
            case 'vec2': return function(v){gl.uniform2f( this.loc, false, v[0], v[1] );};
            case 'mat4': return function(v){gl.uniformMatrix4fv( this.loc, false, v );};
            default:
                throw new Error("unknown uniform type " + type + " in shader " + vs+"+"+fs );
        }
    }

    /** same, for attributes. */
    function getAttributeFN( type ) {
        switch ( type ) {
            case 'vec2': return function(v){ gl.bindBuffer( gl.ARRAY_BUFFER, v ); gl.vertexAttribPointer( this.loc, 2, gl.FLOAT, false, 0, 0 ); };
            case 'vec3': return function(v){ gl.bindBuffer( gl.ARRAY_BUFFER, v ); gl.vertexAttribPointer( this.loc, 3, gl.FLOAT, false, 0, 0 ); };
            case 'vec4': return function(v){ gl.bindBuffer( gl.ARRAY_BUFFER, v ); gl.vertexAttribPointer( this.loc, 4, gl.FLOAT, false, 0, 0 ); };
            default:
                throw new Error("unknown uniform type " + type + " in shader " + vs+"+"+fs );
        }
    }

    /** Utility method to map a regex callback */
    function regexMap(regex, text, callback) {
        while ((result = regex.exec(text)) != null)
            callback(result);
    }

    // extract parameters:

    var src = vs + fs;

    regexMap(/(uniform)\s+(\w+)\s+(\w+)(\[\d+\])?\s*;/g, src, function(groups) {
        var loc = gl.getUniformLocation( shaderProgram, groups[3] );
        if ( loc == null ) {
            console.warn("declared ", groups[0], " not used" );
            return;
        }

        params.uniform[ groups[3] ] = {
            type: groups[2],
            loc:  loc,
            set:  getUniformFN( groups[2] )
       };
    } );

    regexMap(/(attribute)\s+(\w+)\s+(\w+)\s*;/g, src, function(groups) {
        var loc = gl.getAttribLocation ( shaderProgram, groups[3] );
        if ( loc == -1 ) {
            console.warn("declared ", groups[0], " not used" );
            return;
        }
        params.attribute[ groups[3] ] = {
            type: groups[2],
            loc:  loc,
            set:  getAttributeFN( groups[2] )
        };
    } );

    return params;
}

调用此方法,您将可以访问着色器参数,并且可以对其进行迭代或一次性设置所有参数:
var params = initShaderParams( gl, shaderProgram, vsSource, fsSource );

params.uniforms( {
    uViewMatrix: ...,
    uModelMatrix: ...,
    ...
} );

那么,代码就会变得更清晰,更容易发现缺少的内容。您甚至可以编写一个函数来帮助您:计算 params.uniforms键和给定参数的交集,以查看是否指定了所有参数,或者使用setter函数跟踪它们是否已设置。当然,还有许多其他方法;您还可以创建一个Shader类并重写方法。
复制粘贴式编码会导致代码难以维护。作为经验法则,每当您必须编写两次相同的程序时,您就做了一次太多了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接