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