16.光照(平行光)

1.基本概念:

    着色:根据光照条件,重建“物体表面明暗不一效果”的过程

    光源类型:

        (1)平行光:没有衰减的平行的光线,类似于太阳光。用一个方向和一个颜色定义。

        (2)点光源:理想化为质点点光源,类似于人造灯泡,有光线衰减。用光源位置和颜色定义。

        (3)环境光:用于模拟真实世界中的非直射光(由光源发出经过墙壁或其他物体反射的光)。只需要指定颜色。

    反射光取决于以下两个因素:

        (1)入射光(入射光的方向和颜色)

        (2)物体表面类型(物体表面的固有颜色(基底色),和反射特性)

    物体表面反射光线的方式有两种:

        (1)漫反射:针对平行光和点光源而言,漫反射的反射光在各个方向上是均匀的,反射强度受入射角影响。

                我们将入射光线与表面法线形成的夹角为入射角,用B表示,那么

                <漫反射光颜色>=<入射光颜色>x<表面基底色>x cosB,乘法操作在矢量上逐分量进行

        (2)环境反射:针对环境光而言,反射光的方向就是入射光的反方向,所以反射光是各向均匀的。

                <环境反射光颜色>=<入射光颜色>x<表面基底色>

    综上:<表面的反射光颜色>=<漫反射光颜色>+<环境反射光颜色>

2.平行光漫反射的计算:

    平行光:平行光下物体每个面上光线的入射角是相同的,所以只要确定cosB的值再利用公式计算即可。

                 根据向量之间的点乘,a.b = |a|x|b|xcosB,如果a,b向量的模都是1,那么cosB = a.b,那么我们应该首先将a,b向量进行归一化

        (1)归一化:将矢量的长度调整为1,同时保持方向不变

                    GLSL ES提供了归一化函数,其原理是,将矢量的各个分量处以矢量的模,即n( nx/|n| , ny/|n| , nz/|n| )。

        (2)确定法向量方向:

                   法向量表示的是方向,平行平面的法向量相同;每个平面有两个方向相反的法向量,在三维图形学中,法向量方向和顶点的绘制顺序有关,法向量指向顶点绘制顺序逆时针的一方

        cosB = <光线方向>.<法线方向>,其中光线方向是入射光线的反方向(因为该方向与法线夹角才是入射角)。

    <漫反射光颜色>=<入射光颜色>x<表面基底色>x(<光线方向>x<法线方向>)

3.程序实现:

    (1)法向量和光线方向向量归一化:

            由于物体表面的法向量和顶点有关,法向量的归一化放在顶点着色器中进行

'attribute vec4 a_Normal;\n' +

'vec3 normal = normalize(vec3(a_Normal));\n' +

            在GLSL ES中,normalize()方法将矢量进行归一化,由于a_Normal声明为vec4类型,法向量由前三个分量x y z 定义,所以先使用vec3()构造器取出前三个分量

          光线方向矢量归一化:

            光线方向和物体本身无关,所以放在js代码中进行归一化,放在顶点着色器中会执行n遍,浪费计算机资源

    //设置光线方向

    var lightDirection = new Vector3([0.5,3.0,4.0]);

    //入射光线归一化

    lightDirection.normalize();

    gl.uniform3fv(u_LightDirection,lightDirection.elements);

            cuon-matrix.js中为Vector3类型提供了normalize()函数,调用后归一化后的分量值存入对象本身。

    (2)漫反射光颜色计算

   ' float nDotL = max(dot(u_LightDirection,normal),0.0);\n' +

    'vec3 diffuse = u_LightColor * vec3(a_Color) * nDotL;\n'+

        法向量和光线方向向量的点乘运算:dot(n,l ),GLSL  ES内置dot方法计算矢量的点乘,入参为两个矢量,返回点积(标量)

        当点积结果小于0时,说明入射光线方向与法线夹角大于90度,光线照在平面的背面,所以颜色值为0,max(a,b)将a,b中较大的值赋给变量

    (3)环境光的计算

'uniform vec3 u_AmbientLight;\n' +

'vec3 ambient = u_AmbientLight * a_Color.rgb;\n'+

        a_Color是一个vec4类型变量,前三个分量代表rgb值,只取其前三个分量进行计算

    (4)反射光颜色 = 漫反射光颜色+环境反射光颜色

'v_Color = vec4(diffuse + ambient,a_Color.a);\n' +

        最后计算的反射光有三个分量,v_Color有四个分量,将a_Color的a分量值写入

    (5)顶点法向量的写入

            顶点的法向量依赖于顶点组成的平面(TRIANGLE),由多个TRIANGLE共用的顶点在绘制每个TRIANGLE时拥有不同法向量(由顶点绘制顺序决定),例如正六面体中每个顶点被三面共用,那么需要的法向量就有64个

4.绘制光照立方体

var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Color;\n' +
'attribute vec4 a_Normal;\n' +
'uniform mat4 u_MvpMatrix;\n' +
'uniform vec3 u_LightColor;\n' +
'uniform vec3 u_LightDirection;\n' +
'varying vec4 v_Color;\n' +
'void main(){\n' +
'gl_Position = u_MvpMatrix * a_Position;\n' +
//对法向量进行归一化
'vec3 normal = normalize(vec3(a_Normal));\n' +
//计算光线方向和法向量乘积,已经完成归一化,u_LightDirection在js代码中已经完成归一化
'float nDotL = max(dot(u_LightDirection,normal),0.0);\n' +
//计算漫反射光的颜色
'vec3 diffuse = u_LightColor * vec3(a_Color) * nDotL;\n'+
//a_Color.a是GLSL ES中对矢量的访问符,r,g,b,a
'v_Color = vec4(diffuse,a_Color.a);\n' +
'}\n';

var FSHADER_SOURCE =
'precision mediump float;\n'+
'varying vec4 v_Color;\n' +
'void main(){\n' +
'gl_FragColor = v_Color;\n' +
'}\n';

function main(){
var canvas =document.getElementById('webgl');
var gl = canvas.getContext('webgl');
initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE);
var n = initVertexBuffers(gl);
//开启深度检测
gl.enable(gl.DEPTH_TEST);
gl.clearColor(1.0,0.0,0.0,1.0);
var u_LightColor = gl.getUniformLocation(gl.program,'u_LightColor');
var u_LightDirection = gl.getUniformLocation(gl.program,'u_LightDirection');
var u_MvpMatrix = gl.getUniformLocation(gl.program,'u_MvpMatrix');
//设置光线颜色
gl.uniform3f(u_LightColor,1.0,1.0,1.0);
//设置光线方向
var lightDirection = new Vector3([0.5,3.0,4.0]);
//入射光线归一化
lightDirection.normalize();
gl.uniform3fv(u_LightDirection,lightDirection.elements);
//计算模型视图投影矩阵
var mvpMatrix = new Matrix4();
mvpMatrix.setPerspective(30,canvas.width/canvas.height,1,100);
mvpMatrix.lookAt(3,3,7,0,0,0,0,1,0);
//mvpMatrix.rotate(180,0,1,0);
gl.uniformMatrix4fv(u_MvpMatrix,false,mvpMatrix.elements);
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
gl.drawElements(gl.TRIANGLES,n,gl.UNSIGNED_BYTE,0);
}

function initVertexBuffers(gl){
var verteices = new Float32Array([
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, //右面顶点
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, //左面顶点
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 normals = new Float32Array([
0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0,//前面顶点法向量
1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,//右面顶点法向量
0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0,//后面顶点法向量
-1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0,//左面顶点法向量
0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0,//顶面顶点法向量
0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0//底面顶点法向量
]);
var colors = new Float32Array([
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //前面的顶点颜色
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //右面顶点颜色
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //后面顶点颜色
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //左面顶点颜色
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //顶面顶点颜色
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //底面顶点颜色
]);
var indices = new Uint8Array([
0,1,2,0,2,3, //前面的顶点索引
4,5,6,4,6,7, //右面顶点索引
8,9,10,8,10,11, //后面顶点索引
12,13,14,12,14,15, //左面顶点索引
16,17,18,16,18,19, //顶面顶点索引
20,21,22,20,22,23 //底面顶点索引
]);
var indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW);

initArrayBuffer(gl,verteices,3,gl.FLOAT,'a_Position');
initArrayBuffer(gl,colors,3,gl.FLOAT,'a_Color');
initArrayBuffer(gl,normals,3,gl.FLOAT,'a_Normal');
return indices.length;
}
function initArrayBuffer(gl,data,num,type,attribute){
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);
var a_attribute = gl.getAttribLocation(gl.program,attribute);
gl.vertexAttribPointer(a_attribute,num,type,false,0,0);
gl.enableVertexAttribArray(a_attribute);
return true;
}

5.运动物体的光照效果:

    当物体发生平移、旋缩放等变换时,顶点的法向量可能会发生改变,根据模型矩阵计算变换后的法向量的方法是:

        用模型矩阵的逆转置矩阵乘以原法向量,就可以得到变换后的法向量。

    Matrix4提供了计算逆转置矩阵的方法:

        (1)求原矩阵的逆矩阵:

                Matrix4 . setInverseOf(m):将m矩阵的逆矩阵赋值给自身

        (2)对逆矩阵进行转置:

                Matrix4 . transpose():对自身进行操作,将自身设置为转置后的结果。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值