GLSL固定功能片段程序替换 [英] GLSL fixed function fragment program replacement

查看:95
本文介绍了GLSL固定功能片段程序替换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在将OpenGL固定功能管线用于顶点设置时,片段程序看起来与固定功能顶点设置兼容吗?我猜想这通常取决于光源和纹理层的数量等.那么,例如,一个简单的非纹理单光源goraud着色片段程序看起来如何代替GL的固定功能着色器?

解决方案

Gouraud着色计算顶点着色器中的光, Phong着色计算片段着色器中的光. /p>

标准OpenGL光照模型是 Gouraud着色模型,其 Blinn-Phong灯光模型(请勿与 Blinn-Phong 光照模型的计算方法如下这个:

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;

以下函数计算单个Blinn-Phong定向光源:

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; 
}

另请参阅以下问题的答案:

此功能可以应用于顶点着色器,也可以应用于片段着色器.

可以在下面的WebGL示例中找到gouraud着色器程序和phong着色器程序的完整编码:

 (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> 

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 depends on the number of light sources and texture layers etc.. So for example how does a simple non-texture one-lightsource goraud shading fragment program look like that replaces GL's fixed function shader?

解决方案

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:

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>

这篇关于GLSL固定功能片段程序替换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆