1.demo效果
如上图,图二为逐片元处理点光源的效果,与之前的demo效果相比,物体表面的光照效果更佳柔和、细腻。
2.实现要点
在上一章中实现方式是逐顶点的方式,实现点光源在照射到立方体呈现出的效果,通过计算各个顶点在点光源下显现的颜色,然后通过v_Color通过内拆计算出其它片元的颜色,也就是说上一张内插的是color,而这一章内插的是position,通过position计算出点光源方向从而计算出该片元color。
- 对法线进行归一化处理
- 计算点光源照射物体表面的光线方向
- 计算光线方向和法向量点积
- 计算漫反射光的颜色
- 计算环境漫反射光的颜色
- 将漫反射光的颜色和环境漫反射光的颜色相加的结果赋值给内置变量gl_FragColor
3.demo代码
//Chapter 08
//temp 03
//绘制一个立方体
// => 运动中的光照效果
// 加上逆转置变化法向量
const VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_VpMatrix; //view and range mat
uniform mat4 u_ModelMatrix; //model mat
uniform mat4 u_ReverseModelMat; //模型矩阵的逆转置
attribute vec4 a_Normal; //法向量
varying vec3 v_Normal;
varying vec3 v_Position;
uniform vec3 u_LightColor; //平行光
uniform vec3 u_LightPosition;//光源位置
uniform vec3 u_LightDirection; //光照方向 //=归一化后的世界坐标
uniform vec3 u_AmbientLight; // 环境光
varying vec4 v_Color;
void main() {
gl_Position = u_VpMatrix * u_ModelMatrix * a_Position;
//漫反射光颜色
//法向量进行归一化
//计算顶点的世界坐标
v_Position = vec3(u_ModelMatrix*a_Position);
v_Normal=normalize(vec3(u_ReverseModelMat*a_Normal));
v_Color=a_Color;
}
`;
const FSHADER_SOURCE = `
precision mediump float;
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);
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);
// gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;
ready(loadWEBGL);
function loadWEBGL() {
const canvas = document.getElementById("webgl");
const webgl = canvas.getContext("webgl");
if (!initShaders(webgl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log(new Error("failed to init shaders!"));
return;
}
const viewMat = new Matrix4().setLookAt(
7,
2.5,
6,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0
);
const modelMat = new Matrix4().setIdentity();
// var modelMat = new Matrix4().setTranslate(0, 1, 0).rotate(30, 0, 0, 1);
const projMat = new Matrix4();
projMat.setPerspective(30, canvas.width / canvas.height, 1.0, 100);
const vpMat = new Matrix4().setIdentity().multiply(projMat).multiply(viewMat);
const u_VpMatrix = webgl.getUniformLocation(webgl.program, "u_VpMatrix");
webgl.uniformMatrix4fv(u_VpMatrix, false, vpMat.elements);
const u_ModelMatrix = webgl.getUniformLocation(webgl.program, "u_ModelMatrix");
webgl.uniformMatrix4fv(u_ModelMatrix, false, modelMat.elements);
//************* */
//model逆转置
const reverseModelMat = new Matrix4().setInverseOf(modelMat); //求逆
reverseModelMat.transpose(); //转置
const u_ReverseModelMat = webgl.getUniformLocation(
webgl.program,
"u_ReverseModelMat"
);
webgl.uniformMatrix4fv(u_ReverseModelMat, false, reverseModelMat.elements);
//************* */
const u_LightPosition = webgl.getUniformLocation(
webgl.program,
"u_LightPosition"
);
webgl.uniform3f(u_LightPosition, 0.0, 3.0, 4.0);
// data
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_ALLWHITE = 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,
]);
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,
]);
initArrayBuffer(webgl, vertexData, "a_Position", 3, webgl.FLOAT);
initArrayBuffer(webgl, colorData_ALLWHITE, "a_Color", 3, webgl.FLOAT);
initIndexBuffer(webgl, indicesData);
const n = indicesData.length;
//设置光照颜色
const u_LightColor = webgl.getUniformLocation(webgl.program, "u_LightColor");
webgl.uniform3f(u_LightColor, 1.0, 1.0, 1.0);
//设置光照方向
const u_LightDirection = webgl.getUniformLocation(
webgl.program,
"u_LightDirection"
);
const lightDirection = new Vector3([0.5, 3.0, 4.0]);
lightDirection.normalize();
webgl.uniform3fv(u_LightDirection, lightDirection.elements);
//通过设置顶点的法向量 确定面的法向量
const normalData = 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,
]);
initArrayBuffer(webgl, normalData, "a_Normal", 3, webgl.FLOAT);
//设置环境光
const u_AmbientLight = webgl.getUniformLocation(
webgl.program,
"u_AmbientLight"
);
webgl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);
webgl.enable(webgl.DEPTH_TEST);
webgl.clearColor(0.0, 0.0, 0.0, 1.0);
webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
// webgl.drawArrays(webgl.TRIANGLES, 0, n);
webgl.drawElements(webgl.TRIANGLES, n, webgl.UNSIGNED_BYTE, 0);
const tempRMatrix = new Matrix4().setInverseOf(modelMat);
const tick = () => {
var modelMat = animateRotate(webgl, 30);
//model逆转置
tempRMatrix.setInverseOf(modelMat); //求逆
tempRMatrix.transpose(); //转置
webgl.uniformMatrix4fv(u_ReverseModelMat, false, tempRMatrix.elements);
//mvp矩阵
webgl.uniformMatrix4fv(u_ModelMatrix, false, modelMat.elements);
//************* */
webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
webgl.drawElements(webgl.TRIANGLES, n, webgl.UNSIGNED_BYTE, 0);
requestAnimationFrame(tick);
};
// requestAnimationFrame(tick);
}
// 初始化顶点缓冲区
// 坐标值 或者 颜色值
function initArrayBuffer(webgl, data, name, num, type) {
const vertexBuffer = webgl.createBuffer();
webgl.bindBuffer(webgl.ARRAY_BUFFER, vertexBuffer);
webgl.bufferData(webgl.ARRAY_BUFFER, data, webgl.STATIC_DRAW);
const vertexLoction = webgl.getAttribLocation(webgl.program, name);
webgl.vertexAttribPointer(vertexLoction, num, type, false, 0, 0);
webgl.enableVertexAttribArray(vertexLoction);
return true;
}
//=> 使用索引
function initIndexBuffer(webgl, indexData) {
const indicesBuffer = webgl.createBuffer();
webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, indexData, webgl.STATIC_DRAW);
return true;
}
//=> 添加一个动画
// return rotate 矩阵
const CURRENT_ANGLE = 0;
const CURRENT_MODEL_MATRIX = new Matrix4();
const CURRENT_TIMESTAMP = Date.now();
function animateRotate(webgl, speed) {
const now = Date.now();
const interval = Date.now() - CURRENT_TIMESTAMP;
CURRENT_TIMESTAMP = now;
CURRENT_ANGLE += (interval * speed) / 1000; //累加
CURRENT_MODEL_MATRIX.setRotate(CURRENT_ANGLE, 0, 0, 1);
return CURRENT_MODEL_MATRIX;
}