GLSL fixed function fragment program replacement

后端 未结 3 502
独厮守ぢ
独厮守ぢ 2020-11-27 23:16

When using the OpenGL fixed function pipeline for vertex setup, how a fragment program looks like that is compatible to the fixed function vertex setup? I guess that usually

相关标签:
3条回答
  • 2020-11-27 23:46

    While Gouraud shading calculates the light in the the vertex shader, Phong shading calculates the light in the fragment shader.

    The standard OpenGL light model is a Gouraud shading model, with a Blinn-Phong light model (Do not confuse with Phong shading).

    The standard OpenGL Blinn-Phong light model is calcualted like this:

    Ka ... ambient material
    Kd ... difusse material
    Ks ... specular material
    
    La ... ambient light
    Ld ... diffuse light
    Ls ... specular light
    sh ... shininess
    
    N  ... norlmal vector 
    L  ... light vector (from the vertex postion to the light) 
    V  ... view vector (from the vertex psotion to the eye)
    
    Id    = max(dot(N, L), 0.0);
    
    H     = normalize(V + L);
    NdotH = max(dot(N, H), 0.0);
    Is    = (sh + 2.0) * pow(NdotH, sh) / (2.0 * 3.14159265);
    
    fs    = Ka*La + Id*Kd*Ld + Is*Ks*Ls;
    

    The following function calculates a single directional Blinn-Phong light source:

    struct TLightSource
    {
        vec3  lightDir;
        vec3  ambient;
        vec3  diffuse;
        vec3  specular;
        float shininess;
    };
    
    uniform TLightSource u_lightSource;
    
    vec3 Light( vec3 eyeV, vec3 N )
    {
        vec3  lightCol  = u_lightSource.ambient;
        vec3  L         = normalize( -u_lightSource.lightDir );
        float NdotL     = max( 0.0, dot( N, L ) );
        lightCol       += NdotL * u_lightSource.diffuse;
        vec3  H         = normalize( eyeV + L );
        float NdotH     = max( 0.0, dot( N, H ) );
        float kSpecular = ( u_lightSource.shininess + 2.0 ) * pow( NdotH, u_lightSource.shininess ) / ( 2.0 * 3.14159265 );
        lightCol       += kSpecular * u_lightSource.specular;
        return lightCol; 
    }
    

    See also the answers to the following questions:

    • How does this faking the light work on aerotwist?

    This function can be applied to a vertex shader, as to a fragment shader, too.

    The full coding of a gouraud shader program and a phong shader program can be found in the following WebGL example:

    (function loadscene() {
    
    var resize, gl, gouraudDraw, phongDraw, vp_size;
    var bufCube, bufSphere, bufTorus;
    var sliderScale = 100.0;
    
    function render(delteMS){
    
        var ambient_col = hexToRgb( document.getElementById( "ambient_col" ).value );
        var diffuse_col = hexToRgb( document.getElementById( "diffuse_col" ).value );
        var specular_col = hexToRgb( document.getElementById( "specular_col" ).value );
        var ambient = document.getElementById( "ambient" ).value / sliderScale;
        var diffuse = document.getElementById( "diffuse" ).value / sliderScale;
        var specular = document.getElementById( "specular" ).value / sliderScale;
        var shininess = document.getElementById( "shininess" ).value;
        var ambientCol = [ambient_col.r*ambient/256.0, ambient_col.g*ambient/256.0, ambient_col.b*ambient/256.0];
        var diffuseCol = [diffuse_col.r*diffuse/256.0, diffuse_col.g*diffuse/256.0, diffuse_col.b*diffuse/256.0];
        var specularCol = [specular_col.r*ambient/256.0, specular_col.g*ambient/256.0, specular_col.b*ambient/256.0];
        var form = document.getElementById( "form" ).value;
        var shading = document.getElementById( "shading" ).value;
    
        Camera.create();
        Camera.vp = vp_size;
            
        gl.enable( gl.DEPTH_TEST );
        gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
        gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
    
        gl.enable(gl.CULL_FACE);
        gl.cullFace(gl.BACK);
        //gl.frontFace(gl.CW);
        gl.frontFace(gl.CCW);
        
        var progDraw = shading == 0 ? gouraudDraw : phongDraw;;
        // set up draw shader
        ShaderProgram.Use( progDraw.prog );
        ShaderProgram.SetUniformM44( progDraw.prog, "u_projectionMat44", Camera.Perspective() );
        ShaderProgram.SetUniformM44( progDraw.prog, "u_viewMat44", Camera.LookAt() );
        ShaderProgram.SetUniformF3( progDraw.prog, "u_lightSource.lightDir", [-1.0, -0.5, -2.0] )
        ShaderProgram.SetUniformF3( progDraw.prog, "u_lightSource.ambient", ambientCol )
        ShaderProgram.SetUniformF3( progDraw.prog, "u_lightSource.diffuse", diffuseCol )
        ShaderProgram.SetUniformF3( progDraw.prog, "u_lightSource.specular", specularCol )
        ShaderProgram.SetUniformF1( progDraw.prog, "u_lightSource.shininess", shininess )
        var modelMat = IdentityMat44()
        modelMat = RotateAxis( modelMat, CalcAng( delteMS, 13.0 ), 0 );
        modelMat = RotateAxis( modelMat, CalcAng( delteMS, 17.0 ), 1 );
        ShaderProgram.SetUniformM44( progDraw.prog, "u_modelMat44", modelMat );
        
        // draw scene
        bufObj = form == 0 ? bufCube : form == 1 ? bufSphere : bufTorus;
        VertexBuffer.Draw( bufObj );
       
        requestAnimationFrame(render);
    }
    
    function resize() {
        //vp_size = [gl.drawingBufferWidth, gl.drawingBufferHeight];
        vp_size = [window.innerWidth, window.innerHeight]
        canvas.width = vp_size[0];
        canvas.height = vp_size[1];
        gl.viewport( 0, 0, vp_size[0], vp_size[1] );
    }
    
    function initScene() {
    
        document.getElementById( "ambient_col" ).value = "#FFFFFF";
        document.getElementById( "diffuse_col" ).value = "#FFFFFF";
        document.getElementById( "specular_col" ).value = "#FFFFFF";
        document.getElementById( "ambient" ).value = 0.2 * sliderScale;
        document.getElementById( "diffuse" ).value = 0.6 * sliderScale;
        document.getElementById( "specular" ).value = 0.8 * sliderScale;
        document.getElementById( "shininess" ).value = 25.0;
        document.getElementById( "shading" ).value = 0;
        document.getElementById( "form" ).value = 1;
    
        canvas = document.getElementById( "canvas");
        gl = canvas.getContext( "experimental-webgl" );
        if ( !gl )
          return null;
    
        gouraudDraw = {}
        gouraudDraw.prog = ShaderProgram.Create( 
          [ { source : "gouraud-shader-vs", stage : gl.VERTEX_SHADER },
            { source : "gouraud-shader-fs", stage : gl.FRAGMENT_SHADER }
          ],
          [ "u_projectionMat44", "u_viewMat44", "u_modelMat44", 
            "u_lightSource.lightDir", "u_lightSource.ambient", "u_lightSource.diffuse", "u_lightSource.specular", "u_lightSource.shininess", ] );
        if ( gouraudDraw.prog == 0 )
          return;  
        gouraudDraw.inPos = gl.getAttribLocation( gouraudDraw.prog, "inPos" );
        gouraudDraw.inNV  = gl.getAttribLocation( gouraudDraw.prog, "inNV" );
        gouraudDraw.inCol = gl.getAttribLocation( gouraudDraw.prog, "inCol" );
    
        phongDraw = {}
        phongDraw.prog = ShaderProgram.Create( 
          [ { source : "phong-shader-vs", stage : gl.VERTEX_SHADER },
            { source : "phong-shader-fs", stage : gl.FRAGMENT_SHADER }
          ],
          [ "u_projectionMat44", "u_viewMat44", "u_modelMat44", 
            "u_lightSource.lightDir", "u_lightSource.ambient", "u_lightSource.diffuse", "u_lightSource.specular", "u_lightSource.shininess", ] );
        if ( phongDraw.prog == 0 )
          return;
        phongDraw.inPos = gl.getAttribLocation( phongDraw.prog, "inPos" );
        phongDraw.inNV  = gl.getAttribLocation( phongDraw.prog, "inNV" );
        phongDraw.inCol = gl.getAttribLocation( phongDraw.prog, "inCol" );
        
        // create cube
        var cubePos = [
            -1.0, -1.0,  1.0,  1.0, -1.0,  1.0,  1.0,  1.0,  1.0, -1.0,  1.0,  1.0,
            -1.0, -1.0, -1.0,  1.0, -1.0, -1.0,  1.0,  1.0, -1.0, -1.0,  1.0, -1.0 ];
        var cubeCol = [ 1.0, 0.0, 0.0, 1.0, 0.5, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ];
        var cubeHlpInx = [ 0, 1, 2, 3, 1, 5, 6, 2, 5, 4, 7, 6, 4, 0, 3, 7, 3, 2, 6, 7, 1, 0, 4, 5 ]; 
        var cubePosData = [];
        for ( var i = 0; i < cubeHlpInx.length; ++ i ) {
            cubePosData.push( cubePos[cubeHlpInx[i]*3], cubePos[cubeHlpInx[i]*3+1], cubePos[cubeHlpInx[i]*3+2] );
        }
        var cubeNVData = [];
        for ( var i1 = 0; i1 < cubeHlpInx.length; i1 += 4 ) {
            var nv = [0, 0, 0];
            for ( i2 = 0; i2 < 4; ++ i2 ) {
                var i = i1 + i2;
                nv[0] += cubePosData[i*3]; nv[1] += cubePosData[i*3+1]; nv[2] += cubePosData[i*3+2];
            }
            for ( i2 = 0; i2 < 4; ++ i2 )
              cubeNVData.push( nv[0], nv[1], nv[2] );
        }
        var cubeColData = [];
        for ( var is = 0; is < 6; ++ is ) {
            for ( var ip = 0; ip < 4; ++ ip ) {
               cubeColData.push( cubeCol[is*3], cubeCol[is*3+1], cubeCol[is*3+2] ); 
            }
        }
        var cubeInxData = [];
        for ( var i = 0; i < cubeHlpInx.length; i += 4 ) {
            cubeInxData.push( i, i+1, i+2, i, i+2, i+3 );
        }
        bufCube = VertexBuffer.Create(
        [ { data : cubePosData, attrSize : 3, attrLoc : gouraudDraw.inPos },
          { data : cubeNVData, attrSize : 3, attrLoc : gouraudDraw.inNV },
          { data : cubeColData, attrSize : 3, attrLoc : gouraudDraw.inCol } ],
          cubeInxData );
    
        // create sphere
        var layer_size = 16, circum_size = 32;
        var rad_circum = 1.0;
        var rad_tube = 0.5;
        var sphere_pts = [];
        var sphere_nv = [];
        var sphere_col = [];
        sphere_pts.push( 0.0, 0.0, -2.0 );
        sphere_nv.push( 0.0, 0.0, -1.0 );
        //sphere_col.push( 0.8, 0.6, 0.3 );
        sphere_col.push( 0.75, 0.75, 0.75 );
        for ( var i_l = 1; i_l < layer_size; ++ i_l ) {
            var angH = (1.0 - i_l / layer_size) * Math.PI;
            var h = Math.cos( angH );
            var r = Math.sin( angH );
            for ( var i_c = 0; i_c < circum_size; ++ i_c ) {
                var circumX = Math.cos(2 * Math.PI * i_c / circum_size);
                var circumY = Math.sin(2 * Math.PI * i_c / circum_size);
                sphere_pts.push( r * circumX * 2.0, r * circumY * 2.0, h * 2.0 );
                sphere_nv.push( r * circumX, r * circumY, h );
                //sphere_col.push( 0.8, 0.6, 0.3 );
                sphere_col.push( 0.75, 0.75, 0.75 );
            }
        }
        sphere_pts.push( 0.0, 0.0, 2.0 );
        sphere_nv.push( 0.0, 0.0, 1.0 );
        //sphere_col.push( 0.8, 0.6, 0.3 );
        sphere_col.push( 0.75, 0.75, 0.75 );
        var sphere_inx = [];
        for ( var i_c = 0; i_c < circum_size; ++ i_c ) {
            sphere_inx.push( i_c+1, 0, (i_c+1) % circum_size + 1 )
        }
        for ( var i_l = 0; i_l < layer_size-2; ++ i_l ) {
            var l1 = i_l * circum_size + 1;
            var l2 = (i_l+1) * circum_size + 1
            for ( var i_c = 0; i_c < circum_size; ++ i_c ) {
                var i_n = (i_c+1) % circum_size;
                sphere_inx.push( l1+i_c, l1+i_n, l2+i_c, l1+i_n, l2+i_n, l2+i_c );
            }
        }
        for ( var i_c = 0; i_c < circum_size; ++ i_c ) {
            var i_start = 1 + (layer_size-2) * circum_size;
            var i_n = (i_c+1) % circum_size;
            sphere_inx.push( i_start + i_c, i_start + i_n, sphere_pts.length/3-1 );
        }
        bufSphere = VertexBuffer.Create(
        [ { data : sphere_pts, attrSize : 3, attrLoc : gouraudDraw.inPos },
          { data : sphere_nv, attrSize : 3, attrLoc : gouraudDraw.inNV },
          { data : sphere_col, attrSize : 3, attrLoc : gouraudDraw.inCol } ],
          sphere_inx );
    
        // create torus
        var circum_size = 32, tube_size = 32;
        var rad_circum = 1.4;
        var rad_tube = 0.6;
        var torus_pts = [];
        var torus_nv = [];
        var torus_col = [];
        var torus_inx = [];
        var col = [1, 0.5, 0.0];
        for ( var i_c = 0; i_c < circum_size; ++ i_c ) {
            var center = [
                Math.cos(2 * Math.PI * i_c / circum_size),
                Math.sin(2 * Math.PI * i_c / circum_size) ]
            for ( var i_t = 0; i_t < tube_size; ++ i_t ) {
                var tubeX = Math.cos(2 * Math.PI * i_t / tube_size)
                var tubeY = Math.sin(2 * Math.PI * i_t / tube_size)
                var pt = [
                    center[0] * ( rad_circum + tubeX * rad_tube ),
                    center[1] * ( rad_circum + tubeX * rad_tube ),
                    tubeY * rad_tube ]
                var nv = [ pt[0] - center[0] * rad_tube, pt[1] - center[1] * rad_tube, tubeY * rad_tube ]
                torus_pts.push( pt[0], pt[1], pt[2] );
                torus_nv.push( nv[0], nv[1], nv[2] );
                torus_col.push( col[0], col[1], col[2] );
                var i_cn = (i_c+1) % circum_size
                var i_tn = (i_t+1) % tube_size
                var i_c0 = i_c * tube_size; 
                var i_c1 = i_cn * tube_size; 
                torus_inx.push( i_c0+i_t, i_c1+i_t, i_c0+i_tn, i_c0+i_tn, i_c1+i_t, i_c1+i_tn )
            }
        }
        bufTorus = VertexBuffer.Create(
        [ { data : torus_pts, attrSize : 3, attrLoc : gouraudDraw.inPos },
          { data : torus_nv, attrSize : 3, attrLoc : gouraudDraw.inNV },
          { data : torus_col, attrSize : 3, attrLoc : gouraudDraw.inCol } ],
          torus_inx );
          
        window.onresize = resize;
        resize();
        requestAnimationFrame(render);
    }
    
    function Fract( val ) { 
        return val - Math.trunc( val );
    }
    function CalcAng( deltaTime, intervall ) {
        return Fract( deltaTime / (1000*intervall) ) * 2.0 * Math.PI;
    }
    function CalcMove( deltaTime, intervall, range ) {
        var pos = self.Fract( deltaTime / (1000*intervall) ) * 2.0
        var pos = pos < 1.0 ? pos : (2.0-pos)
        return range[0] + (range[1] - range[0]) * pos;
    }    
    function EllipticalPosition( a, b, angRag ) {
        var a_b = a * a - b * b
        var ea = (a_b <= 0) ? 0 : Math.sqrt( a_b );
        var eb = (a_b >= 0) ? 0 : Math.sqrt( -a_b );
        return [ a * Math.sin( angRag ) - ea, b * Math.cos( angRag ) - eb, 0 ];
    }
    
    glArrayType = typeof Float32Array !="undefined" ? Float32Array : ( typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array );
    
    function IdentityMat44() {
      var m = new glArrayType(16);
      m[0]  = 1; m[1]  = 0; m[2]  = 0; m[3]  = 0;
      m[4]  = 0; m[5]  = 1; m[6]  = 0; m[7]  = 0;
      m[8]  = 0; m[9]  = 0; m[10] = 1; m[11] = 0;
      m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1;
      return m;
    };
    
    function RotateAxis(matA, angRad, axis) {
        var aMap = [ [1, 2], [2, 0], [0, 1] ];
        var a0 = aMap[axis][0], a1 = aMap[axis][1]; 
        var sinAng = Math.sin(angRad), cosAng = Math.cos(angRad);
        var matB = new glArrayType(16);
        for ( var i = 0; i < 16; ++ i ) matB[i] = matA[i];
        for ( var i = 0; i < 3; ++ i ) {
            matB[a0*4+i] = matA[a0*4+i] * cosAng + matA[a1*4+i] * sinAng;
            matB[a1*4+i] = matA[a0*4+i] * -sinAng + matA[a1*4+i] * cosAng;
        }
        return matB;
    }
    
    function Cross( a, b ) { return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], 0.0 ]; }
    function Dot( a, b ) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; }
    function Normalize( v ) {
        var len = Math.sqrt( v[0] * v[0] + v[1] * v[1] + v[2] * v[2] );
        return [ v[0] / len, v[1] / len, v[2] / len ];
    }
    
    var Camera = {};
    Camera.create = function() {
        this.pos    = [0, 3, 0.0];
        this.target = [0, 0, 0];
        this.up     = [0, 0, 1];
        this.fov_y  = 90;
        this.vp     = [800, 600];
        this.near   = 0.5;
        this.far    = 100.0;
    }
    Camera.Perspective = function() {
        var fn = this.far + this.near;
        var f_n = this.far - this.near;
        var r = this.vp[0] / this.vp[1];
        var t = 1 / Math.tan( Math.PI * this.fov_y / 360 );
        var m = IdentityMat44();
        m[0]  = t/r; m[1]  = 0; m[2]  =  0;                              m[3]  = 0;
        m[4]  = 0;   m[5]  = t; m[6]  =  0;                              m[7]  = 0;
        m[8]  = 0;   m[9]  = 0; m[10] = -fn / f_n;                       m[11] = -1;
        m[12] = 0;   m[13] = 0; m[14] = -2 * this.far * this.near / f_n; m[15] =  0;
        return m;
    }
    Camera.LookAt = function() {
        var mz = Normalize( [ this.pos[0]-this.target[0], this.pos[1]-this.target[1], this.pos[2]-this.target[2] ] );
        var mx = Normalize( Cross( this.up, mz ) );
        var my = Normalize( Cross( mz, mx ) );
        var tx = Dot( mx, this.pos );
        var ty = Dot( my, this.pos );
        var tz = Dot( [-mz[0], -mz[1], -mz[2]], this.pos ); 
        var m = IdentityMat44();
        m[0]  = mx[0]; m[1]  = my[0]; m[2]  = mz[0]; m[3]  = 0;
        m[4]  = mx[1]; m[5]  = my[1]; m[6]  = mz[1]; m[7]  = 0;
        m[8]  = mx[2]; m[9]  = my[2]; m[10] = mz[2]; m[11] = 0;
        m[12] = tx;    m[13] = ty;    m[14] = tz;    m[15] = 1; 
        return m;
    } 
    
    var ShaderProgram = {};
    ShaderProgram.Create = function( shaderList ) {
        var shaderObjs = [];
        for ( var i_sh = 0; i_sh < shaderList.length; ++ i_sh ) {
            var shderObj = this.CompileShader( shaderList[i_sh].source, shaderList[i_sh].stage );
            if ( shderObj == 0 )
                return 0;
            shaderObjs.push( shderObj );
        }
        var progObj = this.LinkProgram( shaderObjs )
        if ( progObj != 0 ) {
            progObj.attribIndex = {};
            var noOfAttributes = gl.getProgramParameter( progObj, gl.ACTIVE_ATTRIBUTES );
            for ( var i_n = 0; i_n < noOfAttributes; ++ i_n ) {
                var name = gl.getActiveAttrib( progObj, i_n ).name;
                progObj.attribIndex[name] = gl.getAttribLocation( progObj, name );
            }
            progObj.unifomLocation = {};
            var noOfUniforms = gl.getProgramParameter( progObj, gl.ACTIVE_UNIFORMS );
            for ( var i_n = 0; i_n < noOfUniforms; ++ i_n ) {
                var name = gl.getActiveUniform( progObj, i_n ).name;
                progObj.unifomLocation[name] = gl.getUniformLocation( progObj, name );
            }
        }
        return progObj;
    }
    ShaderProgram.AttributeIndex = function( progObj, name ) { return progObj.attribIndex[name]; } 
    ShaderProgram.UniformLocation = function( progObj, name ) { return progObj.unifomLocation[name]; } 
    ShaderProgram.Use = function( progObj ) { gl.useProgram( progObj ); } 
    ShaderProgram.SetUniformI1  = function( progObj, name, val ) { if(progObj.unifomLocation[name]) gl.uniform1i( progObj.unifomLocation[name], val ); }
    ShaderProgram.SetUniformF1  = function( progObj, name, val ) { if(progObj.unifomLocation[name]) gl.uniform1f( progObj.unifomLocation[name], val ); }
    ShaderProgram.SetUniformF2  = function( progObj, name, arr ) { if(progObj.unifomLocation[name]) gl.uniform2fv( progObj.unifomLocation[name], arr ); }
    ShaderProgram.SetUniformF3  = function( progObj, name, arr ) { if(progObj.unifomLocation[name]) gl.uniform3fv( progObj.unifomLocation[name], arr ); }
    ShaderProgram.SetUniformF4  = function( progObj, name, arr ) { if(progObj.unifomLocation[name]) gl.uniform4fv( progObj.unifomLocation[name], arr ); }
    ShaderProgram.SetUniformM33 = function( progObj, name, mat ) { if(progObj.unifomLocation[name]) gl.uniformMatrix3fv( progObj.unifomLocation[name], false, mat ); }
    ShaderProgram.SetUniformM44 = function( progObj, name, mat ) { if(progObj.unifomLocation[name]) gl.uniformMatrix4fv( progObj.unifomLocation[name], false, mat ); }
    ShaderProgram.CompileShader = function( source, shaderStage ) {
        var shaderScript = document.getElementById(source);
        if (shaderScript)
          source = shaderScript.text;
        var shaderObj = gl.createShader( shaderStage );
        gl.shaderSource( shaderObj, source );
        gl.compileShader( shaderObj );
        var status = gl.getShaderParameter( shaderObj, gl.COMPILE_STATUS );
        if ( !status ) alert(gl.getShaderInfoLog(shaderObj));
        return status ? shaderObj : null;
    } 
    ShaderProgram.LinkProgram = function( shaderObjs ) {
        var prog = gl.createProgram();
        for ( var i_sh = 0; i_sh < shaderObjs.length; ++ i_sh )
            gl.attachShader( prog, shaderObjs[i_sh] );
        gl.linkProgram( prog );
        status = gl.getProgramParameter( prog, gl.LINK_STATUS );
        if ( !status ) alert("Could not initialise shaders");
        gl.useProgram( null );
        return status ? prog : null;
    }
    
    var VertexBuffer = {};
    VertexBuffer.Create = function( attributes, indices ) {
        var buffer = {};
        buffer.buf = [];
        buffer.attr = []
        for ( var i = 0; i < attributes.length; ++ i ) {
            buffer.buf.push( gl.createBuffer() );
            buffer.attr.push( { size : attributes[i].attrSize, loc : attributes[i].attrLoc } );
            gl.bindBuffer( gl.ARRAY_BUFFER, buffer.buf[i] );
            gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( attributes[i].data ), gl.STATIC_DRAW );
        }
        buffer.inx = gl.createBuffer();
        gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, buffer.inx );
        gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW );
        buffer.inxLen = indices.length;
        gl.bindBuffer( gl.ARRAY_BUFFER, null );
        gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
        return buffer;
    }
    VertexBuffer.Draw = function( bufObj ) {
      for ( var i = 0; i < bufObj.buf.length; ++ i ) {
            gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.buf[i] );
            gl.vertexAttribPointer( bufObj.attr[i].loc, bufObj.attr[i].size, gl.FLOAT, false, 0, 0 );
            gl.enableVertexAttribArray( bufObj.attr[i].loc );
        }
        gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufObj.inx );
        gl.drawElements( gl.TRIANGLES, bufObj.inxLen, gl.UNSIGNED_SHORT, 0 );
        for ( var i = 0; i < bufObj.buf.length; ++ i )
           gl.disableVertexAttribArray( bufObj.attr[i].loc );
        gl.bindBuffer( gl.ARRAY_BUFFER, null );
        gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
    }
    
    function hexToRgb (hex) {
        // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
        var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
        hex = hex.replace(shorthandRegex, function(m, r, g, b) {
            return r + r + g + g + b + b;
        });
    
        var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return result ? {
            r: parseInt(result[1], 16),
            g: parseInt(result[2], 16),
            b: parseInt(result[3], 16)
        } : null;
    }
    
    initScene();
    
    })();
    <style>
    html,body { margin: 0; overflow: hidden; }
    #gui { position : absolute; top : 0; left : 0; }
    </style>
    <script id="gouraud-shader-vs" type="x-shader/x-vertex">
    precision mediump float;
    
    attribute vec3 inPos;
    attribute vec3 inNV;
    attribute vec3 inCol;
    
    varying vec3 vertPos;
    varying vec3 vertNV;
    varying vec3 vertCol;
    
    uniform mat4 u_projectionMat44;
    uniform mat4 u_viewMat44;
    uniform mat4 u_modelMat44;
    
    struct TLightSource
    {
        vec3  lightDir;
        vec3  ambient;
        vec3  diffuse;
        vec3  specular;
        float shininess;
    };
    
    uniform TLightSource u_lightSource;
    
    vec3 Light( vec3 eyeV, vec3 N )
    {
        vec3  lightCol  = u_lightSource.ambient;
        vec3  L         = normalize( -u_lightSource.lightDir );
        float NdotL     = max( 0.0, dot( N, L ) );
        lightCol       += NdotL * u_lightSource.diffuse;
        vec3  H         = normalize( eyeV + L );
        float NdotH     = max( 0.0, dot( N, H ) );
        float kSpecular = ( u_lightSource.shininess + 2.0 ) * pow( NdotH, u_lightSource.shininess ) / ( 2.0 * 3.14159265 );
        lightCol       += kSpecular * u_lightSource.specular;
        return lightCol; 
    }
    
    void main()
    {
        vec3 modelNV  = mat3( u_modelMat44 ) * normalize( inNV );
        vertNV        = mat3( u_viewMat44 ) * modelNV;
        vec4 modelPos = u_modelMat44 * vec4( inPos, 1.0 );
        vec4 viewPos  = u_viewMat44 * modelPos;
        vertPos       = viewPos.xyz / viewPos.w;
        vec3 eyeV     = normalize( -vertPos );
        vec3 normalV  = normalize( vertNV );
        vertCol       = inCol * Light( eyeV, normalV );
        gl_Position   = u_projectionMat44 * viewPos;
    }
    </script>
    
    <script id="gouraud-shader-fs" type="x-shader/x-fragment">
    precision mediump float;
    
    varying vec3 vertPos;
    varying vec3 vertNV;
    varying vec3 vertCol;
    
    void main()
    {
        gl_FragColor = vec4( vertCol, 1.0 );
    }
    </script>
    
    <script id="phong-shader-vs" type="x-shader/x-vertex">
    precision mediump float;
    
    attribute vec3 inPos;
    attribute vec3 inNV;
    attribute vec3 inCol;
    
    varying vec3 vertPos;
    varying vec3 vertNV;
    varying vec3 vertCol;
    
    uniform mat4 u_projectionMat44;
    uniform mat4 u_viewMat44;
    uniform mat4 u_modelMat44;
    
    void main()
    {
    vec3 modelNV  = mat3( u_modelMat44 ) * normalize( inNV );
    vertNV        = mat3( u_viewMat44 ) * modelNV;
    vertCol       = inCol;
    vec4 modelPos = u_modelMat44 * vec4( inPos, 1.0 );
    vec4 viewPos  = u_viewMat44 * modelPos;
    vertPos       = viewPos.xyz / viewPos.w;
    gl_Position   = u_projectionMat44 * viewPos;
    }
    </script>
    
    <script id="phong-shader-fs" type="x-shader/x-fragment">
    precision mediump float;
    
    varying vec3 vertPos;
    varying vec3 vertNV;
    varying vec3 vertCol;
    
    struct TLightSource
    {
    vec3  lightDir;
    vec3  ambient;
    vec3  diffuse;
    vec3  specular;
    float shininess;
    };
    
    uniform TLightSource u_lightSource;
    
    vec3 Light( vec3 eyeV, vec3 N )
    {
    vec3  lightCol  = u_lightSource.ambient;
    vec3  L         = normalize( -u_lightSource.lightDir );
    float NdotL     = max( 0.0, dot( N, L ) );
    lightCol       += NdotL * u_lightSource.diffuse;
    vec3  H         = normalize( eyeV + L );
    float NdotH     = max( 0.0, dot( N, H ) );
    float kSpecular = ( u_lightSource.shininess + 2.0 ) * pow( NdotH, u_lightSource.shininess ) / ( 2.0 * 3.14159265 );
    lightCol       += kSpecular * u_lightSource.specular;
    return lightCol; 
    }
    
    void main()
    {
    vec3 eyeV    = normalize( -vertPos );
    vec3 normalV = normalize( vertNV );
    vec3 color   = vertCol * Light( eyeV, normalV );
    gl_FragColor = vec4( color, 1.0 );
    }
    </script>
    
    <div>
        <form id="gui" name="inputs">
            <table>
                <tr> <td> <font color= #CCF>ambient</font> </td> 
                        <td> <input type="color" id="ambient_col"></td>
                        <td> <input type="range" id="ambient" min="0" max="100" value="0"/></td> </tr>
                <tr> <td> <font color= #CCF>diffuse</font> </td> 
                        <td> <input type="color" id="diffuse_col"></td>
                        <td> <input type="range" id="diffuse" min="0" max="100" value="0"/></td> </tr>
                <tr> <td> <font color= #CCF>specular</font> </td> 
                        <td> <input type="color" id="specular_col"></td>
                        <td> <input type="range" id="specular" min="0" max="100" value="0"/></td> </tr>
                <tr> <td> <font color= #CCF>shininess</font> </td> 
                        <td> <input type="range" id="shininess" min="0" max="100" value="0"/></td> </tr>
                <tr> <td> <font color= #CCF>form</font> </td> <td>
                    <select id="form">>
                        <option value="0">cube</option>
                        <option value="1">sphere</option>
                        <option value="2">torus</option>
                    </select>
                </td> </tr>
                <tr> <td> <font color= #CCF>shading</font> </td> <td>
                    <select id="shading">>
                        <option value="0">gouraud</option>
                        <option value="1">phong</option>
                    </select>
                </td> </tr>
            </table>
        </form>
    </div>
    
    <canvas id="canvas" style="border: none;"></canvas>

    0 讨论(0)
  • 2020-11-27 23:47

    Well, in the normal GL fixed function pipeline, lighting and goraud shading are done entirely in the vertex stage of the pipeline (with a little bit of help from the rasterizer), so if you have no texturing, the fragment shader becomes a simple pass-through (gl_FragColor = gl_Color;) and doesn't actually do anything.

    Now you CAN do per-fragment lighting, by writing your own fragment shader that does it, and it may even make sense to do that if you want directional spotlights or some such, but that has nothing to do with the fixed function pipeline.

    0 讨论(0)
  • 2020-11-28 00:00

    You might be interested in having a look at ShaderGen. From the description:

    ShaderGen is a program that can automatically produce shaders that mimic the results of a set of fixed function state. This tool was used to verify the fixed function shader code that is described in Chapter 9.

    By the way, you will most likely encounter subtle driver problems if you mix fixed function and GLSL. This is not the "most used way" to do shading so it has a good change of having bugs (at least it did when I tried it). If what you need works, great! Otherwise, you're likely much better off writing the vertex and fragment shaders in GLSL. From what I heard, the driver of modern GPUs uses shaders under the hood anyway to do fixed function.

    0 讨论(0)
提交回复
热议问题