GLSL fixed function fragment program replacement
Asked Answered
F

3

4

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?

Forgiving answered 7/12, 2011 at 20:4 Comment(0)
L
3

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.

Lutero answered 8/12, 2011 at 4:27 Comment(2)
With ShaderGen, it seems that there is no single 'fixed function' fragment shader in OpenGL, but a massive combinatoric amount of code depending on light count, modes, texture stages etc. The question is how they can be switched that fast then, obviously they are not compiled from source after every mode change...Forgiving
The driver writers have access to more bare-metal features than us API users. All or some of these shader combinations can very well be precompiled to GPU machine code in the driver libs which would be very fast to load on-demand or during context creation.Lutero
H
2

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>
Hydrastine answered 16/8, 2017 at 14:20 Comment(0)
C
0

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.

Cidevant answered 7/12, 2011 at 23:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.