光源模型的计算如何在着色器程序中工作? [英] How does the calculation of the light model work in a shader program?

查看:66
本文介绍了光源模型的计算如何在着色器程序中工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试阅读本教程:

如果我们看一下-90°和90°角之间的 cos(x)函数,那么我们可以看到它在0°角处的最大值为1,并且在90°和-90°的角度下降到0.

此行为正是反射模型所需要的.当表面的矢量和光源的方向在同一方向(两者之间的角度为0°)时,我们需要最大的反射率.相反,如果向量是正交的(两者之间的夹角为90°),则我们需要最小的反射,并且我们希望在0°和90°的两个边界之间具有平稳连续的功能.

如果在顶点着色器中计算光源模型,则将为图元的每个角计算反射.在图元之间,根据其重心坐标对反射进行插值.查看球面上的反射结果:

另请参阅:


WebGL示例:球上的朗伯扩散反射

 (function loadcene(){var gl,progDraw,vp_size;var bufSphere = {};函数render(delteMS){Camera.create();Camera.vp = vp_size;gl.viewport(0,0,vp_size [0],vp_size [1]);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);//设置绘制着色器ShaderProgram.Use(progDraw.prog);ShaderProgram.SetUniformM44(progDraw.prog,"u_projectionMat44",Camera.Perspective());ShaderProgram.SetUniformM44(progDraw.prog,"u_viewMat44",Camera.LookAt());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);ShaderProgram.SetUniformF3(progDraw.prog,"u_color",[1.0,0.5,0.0]);ShaderProgram.SetUniformF3(progDraw.prog,"u_lightDir",[-4.0,0.0,-1.0])//绘制场景VertexBuffer.Draw(bufSphere);requestAnimationFrame(render);}函数resize(){//vp_size = [gl.drawingBufferWidth,gl.drawingBufferHeight];vp_size = [window.innerWidth,window.innerHeight]canvas.width = vp_size [0];canvas.height = vp_size [1];}函数initScene(){canvas = document.getElementById("canvas");gl = canvas.getContext("experimental-webgl");如果(!gl)返回null;progDraw = {}progDraw.prog = ShaderProgram.Create([{来源:"draw-shader-vs",舞台:gl.VERTEX_SHADER},{来源:"draw-shader-fs",舞台:gl.FRAGMENT_SHADER}]);如果(!progDraw.prog)返回null;progDraw.inPos = gl.getAttribLocation(progDraw.prog,"inPos");//创建球体var layer_size = 16,circum_size = 32;var rad_circum = 1.0;var rad_tube = 0.5;var sphere_pts = [];sphere_pts.push(0.0,0.0,-1.0);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,r * circumY,h);}}sphere_pts.push(0.0,0.0,1.0);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 +1for(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([{数据:sphere_pts,attrSize:3,attrLoc:progDraw.inPos}],sphere_inx);window.onresize =调整大小;resize();requestAnimationFrame(render);}函数Fract(val){return val-Math.trunc(val);}函数CalcAng(deltaTime,intervall){返回Fract(deltaTime/(1000 * intervall))* 2.0 * Math.PI;}函数CalcMove(deltaTime,intervall,range){var pos = self.Fract(deltaTime/(1000 * intervall))* 2.0var pos = pos<1.0?pos:(2.0-pos)返回范围[0] +(范围[1]-范围[0])* pos;}函数EllipticalPosition(a,b,angRag){var a_b = a * a-b * bvar 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!=未定义"?Float32Array:(typeof WebGLFloatArray!="undefined"?WebGLFloatArray:Array);函数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;返回m;};函数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);对于(var i = 0; i <16; ++ i)matB [i] = matA [i];对于(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;}返回matB;}函数Cross(a,b){返回[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];}函数Dot(a,b){返回a [0] * b [0] + a [1] * b [1] + a [2] * b [2];}函数Normalize(v){var len = Math.sqrt(v [0] * v [0] + v [1] * v [1] + v [2] * v [2]);返回[v [0]/len,v [1]/len,v [2]/len];}var Camera = {};Camera.create = function(){this.pos = [0,1.5,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;返回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;返回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);如果(shderObj == 0)返回0;shaderObjs.push(shderObj);}var progObj = this.LinkProgram(shaderObjs)如果(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);}}返回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);如果(shaderScript)源= shaderScript.text;var shaderObj = gl.createShader(shaderStage);gl.shaderSource(shaderObj,source);gl.compileShader(shaderObj);var status = gl.getShaderParameter(shaderObj,gl.COMPILE_STATUS);如果(!status)alert(gl.getShaderInfoLog(shaderObj));返回状态?shaderObj:null;}ShaderProgram.LinkProgram = function(shaderObjs){var prog = gl.createProgram();对于(var i_sh = 0; i_sh< shaderObjs.length; ++ i_sh)gl.attachShader(prog,shaderObjs [i_sh]);gl.linkProgram(prog);状态= gl.getProgramParameter(prog,gl.LINK_STATUS);if(!status)alert(无法初始化着色器");gl.useProgram(null);返回状态?编:null;}var VertexBuffer = {};VertexBuffer.Create = function(属性,索引){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,新的Float32Array(attribute [i] .data),gl.STATIC_DRAW);}buffer.inx = gl.createBuffer();gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,buffer.inx);gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,新的Uint16Array(索引),gl.STATIC_DRAW);buffer.inxLen = indexs.length;gl.bindBuffer(gl.ARRAY_BUFFER,null);gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,null);返回缓冲区;}VertexBuffer.Draw = function(bufObj){对于(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);对于(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();})();  

  html,body {高度:100%;宽度:100%;边距:0;溢出:隐藏;}#gui {位置:绝对;最高:0;左:0;}  

 <脚本id ="draw-shader-vs" type ="x-shader/x-vertex">精密中型浮子;属性vec3 inPos;变化的vec3 v_normal;统一的mat4 u_projectionMat44;统一的mat4 u_viewMat44;统一的mat4 u_modelMat44;无效main(){vec3 modelNV = mat3(u_modelMat44)* normalize(inPos);vec3 normalV = mat3(u_viewMat44)* modelNV;v_normal = normalV;vec4 modelPos = u_modelMat44 * vec4(inPos,1.0);vec4 viewPos = u_viewMat44 * modelPos;gl_Position = u_projectionMat44 * viewPos;}</script>< script id ="draw-shader-fs" type ="x-shader/x-fragment">精密中型浮子;变化的vec3 v_normal;统一vec3 u_lightDir;统一的vec3 u_color;无效main(){vec3 normalV = normalize(v_normal);vec3 lightV = normalize(-u_lightDir);浮点数NdotL = max(0.0,dot(normalV,lightV));vec3 lightCol =(0.2 + 0.8 * NdotL)* u_color;gl_FragColor = vec4(lightCol.rgb,1.0);}</script>< canvas id ="canvas" style ="border:none;"width ="100%" height ="100%"></canvas>  

I am trying to read up this tutorial:

https://aerotwist.com/tutorials/an-introduction-to-shaders-part-2/

but I am not able to follow up. Basically the code creates a directional light by using shaders that run directly on the GPU. This is the code:

// same name and type as VS
varying vec3 vNormal;

void main() {

    // calc the dot product and clamp
    // 0 -> 1 rather than -1 -> 1
    vec3 light = vec3(0.5,0.2,1.0);

    // ensure it's normalized
    light = normalize(light);

    // calculate the dot product of
    // the light to the vertex normal
    float dProd = max(0.0, dot(vNormal, light));

    // feed into our frag colour
    gl_FragColor = vec4(dProd, dProd, dProd, 1.0);

} 

Specifically, the line that I don't understand is this one:

float dProd = max(0.0, dot(vNormal, light));

How does dot product of vNormal of a vertex and light create a directional light. Can anybody explain me diagrammatically. I am not able to get it . This looks bit of magic to me. I know in this vertex shader each vertex is being passed as an input which is called normal because it is represent in terms of "1" and that shared variable is then used in the above fragment shader code. But apart from this I didn't understand how it works.

P.S: I could have asked to the blog writer but he is on 2 weeks holiday as I know. So I thought someone with some physics or three.js experience might be able to tell me.

解决方案

Lambertian reflectance model

To model the reflection of light in computer graphics is used a Bidirectional reflectance distribution function (BRDF). BRDF is a function that gives the relation between the light reflected along an outgoing direction and the light incident from an incoming direction.

A perfect diffuse surface has a BRDF that has the same value for all incident and outgoing directions. This substantially reduces the computations and thus it is commonly used to model diffuse surfaces as it is physically plausible, even though there are no pure diffuse materials in the real world. This BRDF is called Lambertian reflection because it obeys Lambert's cosine law.

Lambertian reflection is often used as a model for diffuse reflection. This technique causes all closed polygons (such as a triangle within a 3D mesh) to reflect light equally in all directions when rendered The diffusion coefficient is calculated from the angle between the normal vector and the light vector.

f_Lambertian = max( 0.0, dot( N, L )

where N is the normal vector of the surface, and L is the vector towards to the light source.

How it works

In general The dot product of 2 vectors is equal the cosine of the angle between the 2 vectors multiplied by the magnitude (lenght) of both vectors.

dot( A, B ) == length( A ) * length( B ) * cos( angle_A_B ) 

This follows, that the dot product of 2 unit vectors is equal the cosine of the angle between the 2 vectors, because the length of a unit vector is 1.

uA = normalize( A )
uB = normalize( B )
cos( angle_A_B ) == dot( uA, uB )

If we take a look at the cos(x) function between the angles -90° and 90° then we can see that it has a maximum of 1 at an angle of 0° and It goes down to 0 at the angles of 90° and -90°.

This behavior is exactly that what we want for the reflection model. When the nromal vetor of the surface and the diretion to the light source are in the same direction (the angle between is 0°) then we want a maximium of reflection. In contrast, if the vectors a orthonormalized (the angle in between is 90°) then we want a minimum of reflection and we want a smooth and continuous functional running between the two borders of 0° and 90°.

If the light model is calculated in the vertex shader, the reflection is calculated for each corner of the primitive. In between the primitives the reflections are interpolate according to its barycentric coordinates. See the resulting reflections on a spherical surface:

See also:


WebGL example: Lambertian diffuse reflection on a sphere

(function loadscene() {
  
  var gl, progDraw, vp_size;
  var bufSphere = {};
  
  function render(delteMS){

      Camera.create();
      Camera.vp = vp_size;
          
      gl.viewport( 0, 0, vp_size[0], vp_size[1] );
      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 );

      // set up draw shader
      ShaderProgram.Use( progDraw.prog );
      ShaderProgram.SetUniformM44( progDraw.prog, "u_projectionMat44", Camera.Perspective() );
      ShaderProgram.SetUniformM44( progDraw.prog, "u_viewMat44", Camera.LookAt() );
      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 );
      ShaderProgram.SetUniformF3( progDraw.prog, "u_color", [1.0, 0.5, 0.0] );
      ShaderProgram.SetUniformF3( progDraw.prog, "u_lightDir", [-4.0, 0.0, -1.0] )
      
      // 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];
  }
  
  function initScene() {
  
      canvas = document.getElementById( "canvas");
      gl = canvas.getContext( "experimental-webgl" );
      if ( !gl )
        return null;
  
      progDraw = {}
      progDraw.prog = ShaderProgram.Create( 
        [ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
          { source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
        ] );
      if ( !progDraw.prog )
          return null;
      progDraw.inPos = gl.getAttribLocation( progDraw.prog, "inPos" );
      
      // create sphere
      var layer_size = 16, circum_size = 32;
      var rad_circum = 1.0;
      var rad_tube = 0.5;
      var sphere_pts = [];
      sphere_pts.push( 0.0, 0.0, -1.0 );
      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, r * circumY, h );
          }
      }
      sphere_pts.push( 0.0, 0.0, 1.0 );
      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 : progDraw.inPos } ],
          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, 1.5, 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();
  
  })();

html,body {
    height: 100%;
    width: 100%;
    margin: 0;
    overflow: hidden;
}

#gui {
    position : absolute;
    top : 0;
    left : 0;
}

<script id="draw-shader-vs" type="x-shader/x-vertex">
    precision mediump float;
    
    attribute vec3 inPos;
    
    varying vec3 v_normal;

    uniform mat4 u_projectionMat44;
    uniform mat4 u_viewMat44;
    uniform mat4 u_modelMat44;
    
    void main()
    {
        vec3  modelNV = mat3( u_modelMat44 ) * normalize( inPos );
        vec3  normalV = mat3( u_viewMat44 ) * modelNV;
        v_normal      = normalV;

        vec4 modelPos = u_modelMat44 * vec4( inPos, 1.0 );
        vec4 viewPos  = u_viewMat44 * modelPos;
        gl_Position   = u_projectionMat44 * viewPos;
}
</script>
  
<script id="draw-shader-fs" type="x-shader/x-fragment">
    precision mediump float;
    
    varying vec3 v_normal;
    
    uniform vec3 u_lightDir;
    uniform vec3 u_color;

    void main()
    {
        vec3  normalV  = normalize( v_normal );
        vec3  lightV   = normalize( -u_lightDir );
        float NdotL    = max( 0.0, dot( normalV, lightV ) );

        vec3 lightCol  = (0.2 + 0.8 * NdotL) * u_color;
        gl_FragColor   = vec4( lightCol.rgb, 1.0 );
    }
</script>

<canvas id="canvas" style="border: none;" width="100%" height="100%"></canvas>

这篇关于光源模型的计算如何在着色器程序中工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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