WebGL编程指南-23 光照原理、漫反射光计算、漫反射光照射下的立方体

本文介绍了WebGL中漫反射光照的实现过程,包括光照原理、光源类型、反射类型和漫反射计算模型。通过示例代码展示了如何在WebGL中创建带有漫反射光效果的立方体,并详细解释了代码中的关键步骤,如法向量归一化、入射角计算和颜色计算等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.demo效果

在这里插入图片描述
如上,添加了漫反射光照射效果的立方体,最前面的面亮一些,顶上和右侧的面暗一些

2.光照原理

现实中,物体被光线照射,会有一部分光在物体表面反射,当反射光线进入你的眼睛时,你就看到了物体的形状与颜色,现实生活中你会发现,有光照照射物体时会在地面上投下影子,物体表面会出现明暗程度不一样的效果,正是因为存在影子(阴影)明暗差异使得我们看到物体具有立体感。

2.1光源类型

真实世界中存在两种类型的光:平行光和点光源光,平行光类似于太阳光,点光源类似于灯泡、手电筒的光;在WebGL中,光源主要分为三类:平行光、点光源、环境光
平行光的光线相互平行,具有方向,可以看做是无限远处的光源发出的光,列如太阳照射在地球上的光。
点光源是从一个点向周围的所有方向发出的光,例如灯泡和火焰发出的光
环境光也叫做间接光,指光源发出的光被墙壁或物体多次反射后照射的物体表面的光,比如夜间打开冰箱周围都会有微亮的光,环境光不用指定位置和方向,只需要指定颜色即可

2.2反射类型

物体表面反射光线的方式有两种:漫反射和环境反射,漫反射是针对平行光和点光源,环境反射是针对环境光的。

2.3漫反射计算模型

漫反射的决定因素有两个

  • 入射光 入射光的信息包括入射光的方向和颜色
  • 物体表面类型 物体表面信息包括固有颜色(也称基地色 也就是几何体本身的颜色)和反射特性
    <漫反射光颜色> = <入射光颜色> x <表面基底色> x cosθ
    θ表示入射角,即入射光与物体表面法线的夹角
    在创建三维模型的时候我们无法预先确定光线照射到物体表面的入射角,但是我们可以确定物体每个表面的法向量,同时可以指定光源的方向,有了物体表面的法向量和光源信息就可以计算出入射角了。
    我们可以通过计算两个向量的点积来计算这两个向量夹角的余弦值:
    cosθ = <入射光线方向> ● <法线方向>
    将该等式带入漫反射计算模型中得到
    <漫反射光颜色> = <入射光颜色> x <表面基底色> x ( <入射光线方向> ● <法线方向>)
    光线方向矢量和表面法线矢量长度必须为1,也就是使用前必须进行归一化处理,否则反射光的颜色会过暗或过亮

3.demo代码

const VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec4 a_Color;
attribute vec4 a_Normal; //法向量
uniform vec3 u_LightColor; //光线颜色
uniform vec3 u_LightDirection; //光线方向(归一化)
uniform vec3 u_AmbientLight;

uniform mat4 u_MvpMatrix;
varying vec4 v_Color;

void main() {
    gl_Position = u_MvpMatrix*a_Position;
    
    //法线向量归一化
    vec3 normal = normalize(vec3(a_Normal));
    //求夹角  法向与入射光线夹角的余弦值
    float nDotL = max(dot(u_LightDirection,normal),0.0);

    //计算漫反射光的颜色
    vec3 diffuse = u_LightColor*vec3(a_Color)*nDotL;
    //计算环境光产生的反射光颜色
    vec3 ambient = u_AmbientLight*a_Color.rgb;
    v_Color=vec4(diffuse+ambient,1.0);
}
`;

const FSHADER_SOURCE = `
precision mediump float;
varying vec4 v_Color;
void main() {
    gl_FragColor = v_Color;
}
`;

const main = () => {
  const canvas = document.getElementById("webgl");
  const gl = canvas.getContext("webgl");

  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.error("failed to init shaders");
  }
  //视点,视线,上方向
  const viewMat = new Matrix4().setLookAt(
    3,
    3,
    7,
    0.0,
    0.0,
    0.0,
    0.0,
    1.0,
    0.0
  );

  const modelMat = new Matrix4().setRotate(0, 0, 0);

 
  const projMat = new Matrix4();

  projMat.setPerspective(30, canvas.width / canvas.clientHeight, 1.0, 100);

  const mvpMat = new Matrix4()
    .setIdentity()
    .multiply(projMat)
    .multiply(viewMat)
    .multiply(modelMat);
  const u_MvpMatrix = gl.getUniformLocation(gl.program, "u_MvpMatrix");

  const u_LightColor = gl.getUniformLocation(gl.program, "u_LightColor");
  const u_LightDirection = gl.getUniformLocation(
    gl.program,
    "u_LightDirection"
  );

  const u_AmbientLight= gl.getUniformLocation(gl.program,'u_AmbientLight');
  gl.uniform3f(u_AmbientLight,0.2,0.2,0.2);
  
  //设置光的颜色 白色
  gl.uniform3f(u_LightColor, 1.0, 1.0, 1.0);
  //设置光线方向(世界坐标系下的)
  const lightDirection = new Vector3([0.5, 3.0, 4.0]);
  lightDirection.normalize();
  gl.uniform3fv(u_LightDirection, lightDirection.elements);

  gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMat.elements);

  const vertexData = 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, //front面 v0-4
    1.0,
    1.0,
    1.0,
    1.0,
    -1.0,
    1.0,
    1.0,
    -1.0,
    -1.0,
    1.0,
    1.0,
    -1.0, //right v0345
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    -1.0,
    -1.0,
    1.0,
    -1.0,
    -1.0,
    1.0,
    1.0, //up v0561
    -1.0,
    1.0,
    1.0,
    -1.0,
    -1.0,
    1.0,
    -1.0,
    -1.0,
    -1.0,
    -1.0,
    1.0,
    -1.0, //left
    -1.0,
    -1.0,
    1.0,
    1.0,
    -1.0,
    1.0,
    1.0,
    -1.0,
    -1.0,
    -1.0,
    -1.0,
    -1.0, //down
    1.0,
    -1.0,
    -1.0,
    1.0,
    1.0,
    -1.0,
    -1.0,
    1.0,
    -1.0,
    -1.0,
    -1.0,
    -1.0, //back
  ]);

  //   const colorData = new Float32Array([
  //     0.4,0.4,1.0,
  //     0.4,0.4,1.0,
  //     0.4,0.4,1.0,
  //     0.4,0.4,1.0, //front
  //     0.4,1.0,0.4,
  //     0.4,1.0,0.4,
  //     0.4,1.0,0.4,
  //     0.4,1.0,0.4, //right
  //     1.0,0.4,0.4,
  //     1.0,0.4,0.4,
  //     1.0,0.4,0.4,
  //     1.0,0.4,0.4, //up
  //     1.0,1.0,0.4,
  //     1.0,1.0,0.4,
  //     1.0,1.0,0.4,
  //     1.0,1.0,0.4, //left
  //     1.0,0.4,1.0,
  //     1.0,0.4,1.0,
  //     1.0,0.4,1.0,
  //     1.0,0.4,1.0, //btm
  //     0.4,1.0,1.0,
  //     0.4,1.0,1.0,
  //     0.4,1.0,1.0,
  //     0.4,1.0,1.0, //back
  //   ]);

  const colorData = 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, //front
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0, //right
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0, //up
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0, //left
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0, //btm
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0, //back
  ]);
  const indicesData = 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,
  ]);

  const 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, 1.0, 0.0, 0.0, 1.0, 0.0,
    0.0, 1.0, 0.0, 0.0, 1.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, 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, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0,
  ]);

  const initArrayBuffer = (gl, data, name, num, type) => {
    //创建缓冲区
    const vertexBuffer = gl.createBuffer();
    //绑定tag
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    //像缓冲区传入数据
    gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
    const vertexLocation = gl.getAttribLocation(gl.program, name);
    //分配缓冲区
    gl.vertexAttribPointer(vertexLocation, num, type, false, 0, 0);
    //启用缓冲区
    gl.enableVertexAttribArray(vertexLocation);
    return true;
  };

  const initIndexBuffer = (gl, indexData) => {
    const indicesBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW);
  };
  initArrayBuffer(gl, vertexData, "a_Position", 3, gl.FLOAT);
  initArrayBuffer(gl, colorData, "a_Color", 3, gl.FLOAT);
  initArrayBuffer(gl, normals, "a_Normal", 3, gl.FLOAT);

  initIndexBuffer(gl, indicesData);

  const n = indicesData.length;
  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.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);

  let CURRENT_TIMESTAMP = Date.now();
  const tick = (speed) => {
    const now = Date.now();
    const interval = now - CURRENT_TIMESTAMP;
    CURRENT_TIMESTAMP = now;
    const angle = (interval * speed) / 1000;
    modelMat.setRotate(angle, 0, -1, 0);

    const rotateMatrix = gl.getUniformLocation(gl.program,'u_RotateMatrix');
    gl.uniformMatrix4fv(rotateMatrix,false,modelMat.elements);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);  
    requestAnimationFrame(tick);
    console.log('执行了')

    return modelMat;
  };
//   tick()
};

main();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值