Phong and Gouraud Shading WebGL

后端 未结 1 1926
忘了有多久
忘了有多久 2021-01-28 01:55

I read that that in Gouraud Shading, the color for the fragment is computed in the Vertex Shader. Whereas, in Phong Shading, the color for the fragment is computed in the Fragme

相关标签:
1条回答
  • 2021-01-28 02:31

    I did not understand exactly the difference between them. Could anyone help me ?

    The technique used in the code snippet of the question is Gouraud Shading.

    In common Phong shading means the technique, which does the light calculations per fragment, in compare at Gouraud Shading, the light calculations ar done per vertex.

    This means that at Phong shading the light calcualtions ar done in the fragment shader (Not to be confused with Phong reflection model).

    Phong shading:

    At Gouraud Shading the light calculations are done in the vertex shader, for the corners of the primitives (corners of the triangles). The calculated light is (either in a perspective correct manner or linearly) interpolated for all the fragments covered by the primitive, according to the Barycentric coordinate. This increases the performance, but gives a big loss of quality, especially on large primitives and strong specular highlights.

    Gouraud Shading:

    Note, the light is not linear distributed on a surface. If the light is only calculated for some samples and interpolated in between them, this causes flat stains.

    See the example, which compares the 2 techniques:

    (function loadscene() {
    
    var resize, gl, gouraudDraw, phongDraw, vp_size;
    var bufSphere = {};
    
    function render(delteMS){
    
        var shading = document.getElementById( "shading" ).value;
        var shininess = document.getElementById( "shininess" ).value;
        var ambientCol = [0.2, 0.2, 0.2];
        var diffuseCol = [0.6, 0.6, 0.6];
        var specularCol = [0.8, 0.8, 0.8];
    
        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
        VertexBuffer.Draw( bufSphere );
       
        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() {
    
        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 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 );
        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_pts.push( 0.0, 0.0, 2.0 );
        sphere_nv.push( 0.0, 0.0, 1.0 );
        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 );
          
        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 );
    }
    
    initScene();
    
    })();
    <style>
    html,body {
        height: 100%;
        width: 100%;
        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>
    
    <form id="gui" name="inputs"><table><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><tr>
        <td><font color= #CCF>Shininess:</font></td>
        <td><input type="range" id="shininess" min="0" max="100" value="20"/></td>
    </tr></table></form>
    <canvas id="canvas" style="border: none;" width="100%" height="100%"></canvas>

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