着色器程序中光照模型的计算是如何进行的?

发布于 2024-11-29 08:08:13 字数 1105 浏览 2 评论 0原文

我正在尝试阅读本教程:

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

但我无法跟进。基本上,代码通过使用直接在 GPU 上运行的着色器来创建定向光。这是代码:

// 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);

} 

具体来说,我不明白的一行是:

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

How does dot product of vNormal of a vertex and light create a orienting light.谁能给我用图表解释一下。我无法得到它。这对我来说看起来有点神奇。我知道在这个顶点着色器中,每个顶点都作为输入传递,称为正常,因为它以“1”表示,然后在上面的片段着色器代码中使用该共享变量。但除此之外我不明白它是如何工作的。

PS:我本可以询问博客作者,但据我所知,他正在休假两周。所以我想有一些物理或 Three.js 经验的人也许可以告诉我。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

顾挽 2024-12-06 08:08:13

朗伯反射模型

为了对计算机图形学中的光反射进行建模,使用了双向反射分布函数 (BRDF)。
BRDF 是给出沿出射方向反射的光与从入射方向入射的光之间关系的函数。

完美的漫反射表面的 BRDF 对于所有入射和出射方向都具有相同的值。这大大减少了计算量,因此通常用于对漫反射表面进行建模,因为它在物理上是合理的,即使现实世界中不存在纯漫反射材料。这个 BRDF 被称为朗伯反射,因为它遵循朗伯余弦定律。

朗伯反射通常用作漫反射的模型。此技术使所有闭合多边形(例如 3D 网格内的三角形)在渲染时在所有方向上均匀地反射光线。漫射系数是根据法线矢量和光矢量之间的角度计算的。

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

其中N是表面的法线向量,L是朝向光源的向量。

工作原理

一般来说,2 个向量的点积等于 2 个向量之间的角度的余弦乘以两个向量的大小(长度)。

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

由此可见,2 个单位向量的点积等于 2 个向量之间角度的余弦,因为单位向量的长度为 1。

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

A 点 B

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

余弦[-90°, 90°]

这种行为正是我们想要的反射模型。当表面的正常矢量和光源的方向处于同一方向(之间的角度为 0°)时,我们需要最大的反射。
相反,如果向量正交化(之间的角度为 90°),那么我们想要最小的反射,并且我们想要在 0° 和 90° 的两个边界之间平滑且连续的函数运行。

N 点L

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

在此处输入图像描述

另请参阅:

WebGL 示例:球体上的朗伯漫反射

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

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 )

A dot B

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°.

cosin in [-90°, 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°.

N dot L

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:

enter image description here

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>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文