WebGl点光、漫反射和汇总

/**
    关键字:WebGL 模型矩阵 视图矩阵 逆转置矩阵    逐片元 
**/

//顶点着色器
var VSHADER_SOURCE =
  'attribute vec4 a_Position;
' + //顶点数据
  'attribute vec4 a_Normal;
' +    //法线数据
  'uniform mat4 u_MvpMatrix;
' + //模型视图投影矩阵
  'uniform mat4 u_ModelMatrix;
' +    // 模型矩阵
  'uniform mat4 u_NormalMatrix;
' +   // 模型的逆转置矩阵,用于计算变化后的法线。
  'varying vec4 v_Color;
' + //顶点颜色
  'varying vec3 v_Normal;
' +    //变化之后的顶点的法线
  'varying vec3 v_Position;
' + //世界坐标系中的顶点位置
  'void main() {
' +
  '  vec4 color = vec4(1.0, 1.0, 1.0, 1.0);
' + // 球体颜色
  '  gl_Position = u_MvpMatrix * a_Position;
' + //球的位置
  '  v_Position = vec3(u_ModelMatrix * a_Position);
' + //计算顶点在世界坐标系中的位置。
  '  v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));
' +//计算变化之后的顶点法线
  '  v_Color = color;
' + //顶点颜色
  '}
';

// 片元着色器
var FSHADER_SOURCE =
  '#ifdef GL_ES
' +
  'precision mediump float;
' +
  '#endif
' +
  'uniform vec3 u_LightColor;
' +     // 灯光颜色
  'uniform vec3 u_LightPosition;
' +  // 光源的位置
  'uniform vec3 u_AmbientLight;
' +   // 环境光颜色
  'varying vec3 v_Normal;
' +    //顶点法线
  'varying vec3 v_Position;
' + //顶点位置
  'varying vec4 v_Color;
' + //顶点颜色
  'void main() {
' +
     //再次对法线归一化,因为被插值了。
  '  vec3 normal = normalize(v_Normal);
' + 
     //计算光的方向并归一化
  '  vec3 lightDirection = normalize(u_LightPosition - v_Position);
' +//计算
     //用点积计算光与法线夹角的cos值。
  '  float nDotL = max(dot(lightDirection, normal), 0.0);
' +
     //计算漫反射光
  '  vec3 diffuse = u_LightColor * v_Color.rgb * nDotL;
' +
     //计算环境光
  '  vec3 ambient = u_AmbientLight * v_Color.rgb;
' +
     //最终的颜色
  '  gl_FragColor = vec4(diffuse + ambient, v_Color.a);
' +
  '}
';

function main() {
  // Retrieve <canvas> element
  var canvas = document.getElementById('webgl');

  // Get the rendering context for WebGL
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('Failed to intialize shaders.');
    return;
  }

  //创建球的顶点缓存
  var n = initVertexBuffers(gl);
  if (n < 0) {
    console.log('Failed to set the vertex information');
    return;
  }

  // Set the clear color and enable the depth test
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.enable(gl.DEPTH_TEST);

  // Get the storage locations of uniform variables
  var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
  var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
  var u_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix');
  var u_LightColor = gl.getUniformLocation(gl.program, 'u_LightColor');
  var u_LightPosition = gl.getUniformLocation(gl.program, 'u_LightPosition');
  var u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight');
  if (!u_ModelMatrix || !u_MvpMatrix || !u_NormalMatrix || !u_LightColor || !u_LightPosition || !u_AmbientLight) { 
    console.log('Failed to get the storage location');
    return;
  }

  // Set the light color (white)
  gl.uniform3f(u_LightColor, 0.8, 0.8, 0.8);
  // Set the light direction (in the world coordinate)
  gl.uniform3f(u_LightPosition, 5.0, 8.0, 7.0);
  // Set the ambient light
  gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);

  var modelMatrix = new Matrix4();  // Model matrix
  var mvpMatrix = new Matrix4();    // Model view projection matrix
  var normalMatrix = new Matrix4(); // Transformation matrix for normals

  // Calculate the model matrix
  modelMatrix.setRotate(90, 0, 1, 0); // Rotate around the y-axis
  // Calculate the view projection matrix
  mvpMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
  mvpMatrix.lookAt(0, 0, 6, 0, 0, 0, 0, 1, 0);
  mvpMatrix.multiply(modelMatrix);//对球来说,旋转是多余的,没啥效果。
  // Calculate the matrix to transform the normal based on the model matrix
  normalMatrix.setInverseOf(modelMatrix);//求逆
  normalMatrix.transpose();//转置

  // Pass the model matrix to u_ModelMatrix
  gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);

  // Pass the model view projection matrix to u_mvpMatrix
  gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

  // Pass the transformation matrix for normals to u_NormalMatrix
  gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);

  // Clear color and depth buffer
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  // 绘制三角形构成的立方体
  gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0);
}

function initVertexBuffers(gl) { // Create a sphere
  var SPHERE_DIV = 13;

  var i, ai, si, ci;
  var j, aj, sj, cj;
  var p1, p2;

  var positions = [];
  var indices = [];

  // Generate coordinates
  for (j = 0; j <= SPHERE_DIV; j++) {
    aj = j * Math.PI / SPHERE_DIV;
    sj = Math.sin(aj);
    cj = Math.cos(aj);
    for (i = 0; i <= SPHERE_DIV; i++) {
      ai = i * 2 * Math.PI / SPHERE_DIV;
      si = Math.sin(ai);
      ci = Math.cos(ai);

      positions.push(si * sj);  // X
      positions.push(cj);       // Y
      positions.push(ci * sj);  // Z
    }
  }

  // Generate indices//产生上面这些数据的索引。
  for (j = 0; j < SPHERE_DIV; j++) {
    for (i = 0; i < SPHERE_DIV; i++) {
      p1 = j * (SPHERE_DIV+1) + i;
      p2 = p1 + (SPHERE_DIV+1);

      indices.push(p1);
      indices.push(p2);
      indices.push(p1 + 1);

      indices.push(p1 + 1);
      indices.push(p2);
      indices.push(p2 + 1);
    }
  }

  // Write the vertex property to buffers (coordinates and normals)
  // Same data can be used for vertex and normal
  // In order to make it intelligible, another buffer is prepared separately
  if (!initArrayBuffer(gl, 'a_Position', new Float32Array(positions), gl.FLOAT, 3)) return -1;
  if (!initArrayBuffer(gl, 'a_Normal', new Float32Array(positions), gl.FLOAT, 3))  return -1;
  
  // Unbind the buffer object
  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  // Write the indices to the buffer object
  var indexBuffer = gl.createBuffer();
  if (!indexBuffer) {
    console.log('Failed to create the buffer object');
    return -1;
  }
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

  return indices.length;
}

function initArrayBuffer(gl, attribute, data, type, num) {
  // Create a buffer object
  var buffer = gl.createBuffer();
  if (!buffer) {
    console.log('Failed to create the buffer object');
    return false;
  }
  // Write date into the buffer object
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
  // Assign the buffer object to the attribute variable
  var a_attribute = gl.getAttribLocation(gl.program, attribute);
  if (a_attribute < 0) {
    console.log('Failed to get the storage location of ' + attribute);
    return false;
  }
  gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
  // Enable the assignment of the buffer object to the attribute variable
  gl.enableVertexAttribArray(a_attribute);

  return true;
}


相关资料

相关资料:

第二章

    零、顶点着色器:控制位置和大小。
        片元着色器:控制颜色。

    一、顶点着色器内置变量:
        1、gl_Position,
        2、gl_PointSize

    二、片元着色器内置变量
        FragColor(片元着色器唯一的变量)。
        
    三、gl.drawArrays(mode,first,count)
        mode:gl.POINTS,
             gl.LINES,gl.LINE_STRIP((1,2),(2,3) ...),
             gl.LINE_LOOP((1,2),(2,3) ...首尾相连),
             gl.TRIANGLES,
             gl.TRIANGLE_STRIP((0,1,2),(2,1,3),(2,3,4)...),
             gl.TRIANGLE_FAN((1,2,3),(1,3,4),(1,4,5))。
        first:从哪个点开始。
        count:使用多少个点。
        注意:当该函数被执行时,顶点着色器被执行count次。每次处理一个点。
    四、webGL使用的是右手坐标系:x向右、y向上、z向屏幕外。

    五、从js向顶点着色器传变量的方式有两个:
        1、attribute(存储限定符) 传输那些和顶点相关的数据。(只有顶点着色器能使用)
            使用的过程:
                a)在顶点着色器中声明attribute类型的变量,
                b)赋值给gl_Position,c), 
                c)在js变量中获取这个变量的存储地址: gl.getAttribLocation(gl.program, 'a_Position'),
                d)在js中给这个变量赋值: gl.vertexAttrib3f(a_Position, 0.0, 0.5, 0.0)。
                
        2、uniform(存储限定符)    传输哪些对于所有顶点都相同(和顶点无关)的数据。(顶点着色器和片元着色器都可以用)
            使用的过程和attribute的套路差不多。
第三章 绘制和变换三角形
    一、创建缓存区对象的过程:
            1、准备好数据-> var vertices = new float32Array([0.0,0.5,-0.5,-0.5,0.5,-0.5]);  //三个点
            2、创建一个缓存区对象->  var vertexBuffer = gl.createBuffer();
                3、[将缓存区绑定到目标]-> gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer);
            4、将数据写到缓存区里-> gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW);
            5、将缓存区的数据赋给顶点着色器-> gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0);
                6、[让分配的变量可用]。gl.enableVertexAttribArray(a_Position);
                
    矩阵:每次变化,都是在顶点着色器中逐顶点改变图形的位置。
            每一次新的变换,都得求一个新的等式放到顶点着色器中实现,为了不这样来回更改顶点着色器,我们使用新的工具
            --变化矩阵。将多个变化矩阵一乘(变换效果叠加)得到一个总变化矩阵,再传入顶点着色器(矩阵*矢量特性)。
            变化矩阵非常适合操作计算机图形。
            
    二、旋转矩阵
            // Note: WebGL is column major order 列主序!!!!!
            var xformMatrix = new Float32Array([
             cosB, sinB, 0.0, 0.0,
            -sinB, cosB, 0.0, 0.0,
              0.0,  0.0, 1.0, 0.0,
              0.0,  0.0, 0.0, 1.0
            ]);
            
            //行主序
            [
                cosB, -sinB, 0.0, 0.0,
                sinB, cosB,  0.0, 0.0,
                0.0,  0.0,   1.0, 0.0,
                0.0,  0.0,   0.0, 1.0
            
            ]
    三、平移矩阵
            var xformMatrix = new Float32Array([
             1.0,  0.0,  0.0,  0.0,
             0.0,  1.0,  0.0,  0.0,
             0.0,  0.0,  1.0,  0.0,
             Tx,    Ty,   Tz,  1.0
            ]);
            
            //行主序
            [
                1.0, 0.0, 0.0, Tx,
                0.0, 1.0, 0.0, Ty,
                0.0, 0.0, 1.0, Tz,
                0.0, 0.0, 0.0, 1.0
            ]
    四、缩放矩阵
            var xformMatrix = new Float32Array([
             Sx,  0.0,  0.0,  0.0,
             0.0,  Sy,  0.0,  0.0,
             0.0,  0.0,  Sz,  0.0,
             0.0,  0.0, 0.0,  1.0
            ]);
            
            //行主序
            [
                Sx, 0.0, 0.0, 0.0,
                0.0, Sy, 0.0, 0.0,
                0.0, 0.0, Sz, 0.0,
                0.0, 0.0,0.0,1.0
            ]
第四章 高级变换与动画基础
    一、模型矩阵:一个模型可能经过多次变换,将这些变化全部复合成一个等效的变换(矩阵之间相乘)就得到了模型变换。
                相应的,模型变化的矩阵称之为模型矩阵。
    二、一般情况下,平移旋转和旋转平移所得到的效果是不一样的(比如沿x轴平移和沿x旋转,效果应该是一样的)。

第五章 颜色和纹理

    顶点着色器(逐顶点操作)-->装配(什么形状)-->光栅化(插值,什么颜色)-->片元着色器(逐片元操作)。
    一、多种顶点数据传入顶点着色器的方法:
        1、建立多个缓存区分别传入。
        2、建立一个缓存区,将数据交叉传入(利用gl.vertexAttribPointer
        (a_PointSize, 1, gl.FLOAT, false, FSIZE * 3, FSIZE * 2);)。
    二、varying(可变量):从顶点着色器向片元着色器传输数据。在WebGl中,如果顶点着色器和片元着色器中有类型和名称都相同的
            varying变量,那么顶点着色器赋值给该变量的值会被自动传入片元着色器。
            只能是float以及与之相关的顶点(vec2,vec3,vec4)矩阵(mat2,mat3,mat4)。
    三、片元坐标:光栅化的片元都带有坐标信息的,gl_FragCoord.x,gl_FragCoord.y。
    四、纹理映射的四个步骤:
        1、准备好纹理图像。(图片的坐标系左上角为原点,WebGL则是左下角为原点)
        2、为几何图形配置纹理映射方式。
        3、加载纹理图像,对其进行一些配置,以在webGl中用它。
        4、在片元着色器中将相应的纹素从纹理中抽取出来,并将纹素的颜色赋给片元。
        
    五、使用纹理的详细步骤
        1、设置纹理坐标
            将顶点坐标、纹理坐标都存到缓存区别开启。
        2、配置和加载纹理
            创建了纹理对象,绑定了onload事件。
        3、为WebGL配置纹理。
            开启0号纹理单元
            绑定纹理对象
            配置纹理对象的参数
            将纹理图像分配给纹理对象
            将0号纹理传递给片元着色器中的取样变量。
        4、从顶点着色器向片元着色器传递纹理坐标。
        5、从片元着色器中获取纹理像素颜色。
第七章 进入三维世界

    "根据自定义观察者的状态,绘制观察者看到的景象"(将视图矩阵传入顶点着色器) 和 
    "使用默认观察者的状态,对三维对象进行变换(平移、旋转等),再绘制观察者看到的景象"(
    模型矩阵传入顶点着色器)效果是一样的。(反正都要在顶点着色器中乘一个矩阵)
    
    WebGL默认视点:在(0,0,0)点,看向Y轴负半轴,即朝屏幕里面。
    视图矩阵:视点、观察目标点、上方向。
        用视图矩阵乘以顶点坐标会把顶点变化到合适的位置,使得观察者(以默认的状态)观察新位置的顶点。就好像
        在观察者处在(视图矩阵描述的)视点上观察原始顶点一样。
        (最终的结果是 :视点还在原点,只是物体的为矩阵到合适的位置)
    模型矩阵:平移、缩放、旋转等基本变化矩阵或它们的组合。
    模型视图矩阵:视图矩阵*模型矩阵。
    
    正射投影矩阵:Matrix.setOrtho(left,right,bottom,top,near,far)。
    透视投影矩阵:Matrix.setPerspective(fov,aspect,near,far)
        fov:垂直视角,可视空间顶面与地面的夹角。
        aspect:近裁剪面的宽高比(宽度/高度)。
        near,far:近裁剪面和远裁剪面的位置(相对于视点,必须大于零)。
        
    世界物体-->裁剪(投影矩阵)  -->规范立方体-->降维-->屏幕上。
        
    模型视图投影矩阵:投影矩阵*视图矩阵*模型矩阵(投影矩阵的顺序位置不能移动,为什么?)
        
第八章 光照
    看到的光(物体反射的光)取决于以下两个因素:
        入射光(方向和颜色)。
            
        物体表面的类型(基色和反射特性)。
        反射特性:
            漫反射:反射光在各方向上均匀的,是一种理想模型。
                漫反射反射光的颜色:入射光颜色*表面基色*cosB,B是入射光与表面法线的夹角。
                                    =入射光颜色*表面基色*(光线方向.法线方向)
            环境反射:反射光在各方向上均匀的,强度相等的。
                环境反射光颜色:入射光颜色*表面基色。
    看见的光 = 漫反射反射光的颜色 + 环境反射光颜色
    
    计算变化之后的法向量:之前的法向量*模型矩阵的逆转置矩阵。
    逆转置矩阵:逆矩阵的转置。
原文地址:https://www.cnblogs.com/airduce/p/11686138.html