第七步 实时球谐光照计算
歪打正着
function getRotationPrecomputeL(precompute_L, rotationMatrix){
result = [];
for(xx = 0;xx < 3;xx++)
{
result[xx] = [];
rr = [];
for(yy =0;yy < 9;yy ++)
{
result[xx][yy] = precompute_L[yy][xx];
rr[yy] = rotationMatrix[(yy - (yy % 3)) / 3 * 4 + (yy % 3)];
// console.log(rotationMatrix[(yy - (yy % 3)) / 3 * 4 + (yy % 3)]);
// console.log(rotationMatrix);
}
console.log("result[xx]" + result[xx]);
console.log("rr" + rr);
// result[xx] = mat3.create(result[xx]).reshape(3,3);
// rr = mat3.create(rr).reshape(3,3);
console.log(math.multiply([1,0,0,0,1,1,0,0,1] ,[1,0,0,0,1,0,0,0,1]));
}
return result;
}
我这时候正在打印,突然能用了
发现传入的数据其实是一个 9维的向量
我之前把他打包成3x3的矩阵是有问题的
let precomputeL_RGBMat3 = getRotationPrecomputeL(precomputeL[guiParams.envmapId], cameraModelMatrix);
if (k == 'uPrecomputeLR') {
gl.uniformMatrix3fv(
this.meshes[i].shader.program.uniforms[k],
false,
precomputeL_RGBMat3[0]);
}
if (k == 'uPrecomputeLG') {
gl.uniformMatrix3fv(
this.meshes[i].shader.program.uniforms[k],
false,
precomputeL_RGBMat3[1]);
}
if (k == 'uPrecomputeLB') {
gl.uniformMatrix3fv(
this.meshes[i].shader.program.uniforms[k],
false,
precomputeL_RGBMat3[2]);
}
实际上 gl.uniformMatrix3fv函数
已经帮我把9维的向量打包成 Mat3了
效果
第八步 环境光球谐旋转 (bonus)
现在 我拿到了SH 9个系数 ,和3x3旋转矩阵,怎么旋转呢?
首先precomputeL是SH函数,拿到9个系数就相当于拿到了原始图片。
precomputeL存储的其实是环境光球谐系数下的 RGB 值;
那么对环境光旋转的问题就转化为对SH函数进行旋转
第一个性质
球谐函数具有旋转不变形性,
第二个性质
对每层 band 上的 SH coefficient,可以分别在上面进行旋转,并且这个旋转是线性变化。
band就是阶数
公式1
性质推 存在mat4 M 使得公式成立
其中
- M是三维空间旋转矩阵 mat4
- R是旋转函数
- n是三维空间法线向量 vec3
- P是获取该bind上的球谐投影系数的函数
此处假设A是可逆矩阵,实际上要判断A是否可逆,要看A的行列式不等于0;
如何选取2l + 1个法向量
computeSquareMatrix_3by3
computeSquareMatrix_5by5
这两个预设函数里已经给了,我还在傻乎乎的想!!
二阶情况下 3x3
let n1 = [1, 0, 0, 0]; let n2 = [0, 0, 1, 0]; let n3 = [0, 1, 0, 0];
let p1 = sh.SHEval3(n1[0],n1[1],n1[2]);
let p2 = sh.SHEval3(n2[0],n2[1],n2[2]);
let p3 = sh.SHEval3(n3[0],n3[1],n3[2]);
let A_mat = math.matrix([[p1[1],p1[2],p1[3]],
[p2[1],p2[2],p2[3]],
[p3[1],p3[2],p3[3]]]
);
let A_inverse_mat = math.inv(A_mat);
let n1_rot = vec4.create();
let n2_rot = vec4.create();
let n3_rot = vec4.create();
vec4.transformMat4(n1_rot, n1, rotationMatrix);
vec4.transformMat4(n2_rot, n2, rotationMatrix);
vec4.transformMat4(n3_rot, n3, rotationMatrix);
let p1_rot = sh.SHEval3(n1_rot[0],n1_rot[1],n1_rot[2]);
let p2_rot = sh.SHEval3(n2_rot[0],n2_rot[1],n2_rot[2]);
let p3_rot = sh.SHEval3(n3_rot[0],n3_rot[1],n3_rot[2]);
let S_mat = math.matrix([[p1_rot[1],p1_rot[2],p1_rot[3]],
[p2_rot[1],p2_rot[2],p2_rot[3]],
[p3_rot[1],p3_rot[2],p3_rot[3]]]
);
vec4.transformMat4(n3_rot, n3, rotationMatrix);
其实就是对 n3 右乘 rotationMatrix 得到 n3_rot
result = S_mat * A_inverse_mat;
三阶情况下 5x5
function computeSquareMatrix_5by5(rotationMatrix){ // 计算方阵SA(-1) 5*5
// 1、pick ni - {ni}
let k = 1 / math.sqrt(2);
let n1 = [1, 0, 0, 0]; let n2 = [0, 0, 1, 0]; let n3 = [k, k, 0, 0];
let n4 = [k, 0, k, 0]; let n5 = [0, k, k, 0];
let p1 = sh.SHEval3(n1[0],n1[1],n1[2]);
let p2 = sh.SHEval3(n2[0],n2[1],n2[2]);
let p3 = sh.SHEval3(n3[0],n3[1],n3[2]);
let p4 = sh.SHEval3(n4[0],n4[1],n4[2]);
let p5 = sh.SHEval3(n5[0],n5[1],n5[2]);
let A_mat = math.matrix([[p1[4],p1[5],p1[6],p1[7],p1[8]],
[p2[4],p2[5],p2[6],p2[7],p2[8]],
[p3[4],p3[5],p3[6],p3[7],p3[8]],
[p4[4],p4[5],p4[6],p4[7],p4[8]],
[p5[4],p5[5],p5[6],p5[7],p5[8]]]
);
// 2、{P(ni)} - A A_inverse
let A_inverse_mat = math.inv(A_mat);
// 3、用 R 旋转 ni - {R(ni)}
let n1_rot = vec4.create();
let n2_rot = vec4.create();
let n3_rot = vec4.create();
let n4_rot = vec4.create();
let n5_rot = vec4.create();
vec4.transformMat4(n1_rot, n1, rotationMatrix);
vec4.transformMat4(n2_rot, n2, rotationMatrix);
vec4.transformMat4(n3_rot, n3, rotationMatrix);
vec4.transformMat4(n4_rot, n4, rotationMatrix);
vec4.transformMat4(n5_rot, n5, rotationMatrix);
// 4、R(ni) SH投影 - S
let p1_rot = sh.SHEval3(n1_rot[0],n1_rot[1],n1_rot[2]);
let p2_rot = sh.SHEval3(n2_rot[0],n2_rot[1],n2_rot[2]);
let p3_rot = sh.SHEval3(n3_rot[0],n3_rot[1],n3_rot[2]);
let p4_rot = sh.SHEval3(n4_rot[0],n4_rot[1],n4_rot[2]);
let p5_rot = sh.SHEval3(n5_rot[0],n5_rot[1],n5_rot[2]);
let S_mat = math.matrix([[p1_rot[4],p1_rot[5],p1_rot[6],p1_rot[7],p1_rot[8]],
[p2_rot[4],p2_rot[5],p2_rot[6],p2_rot[7],p2_rot[8]],
[p3_rot[4],p3_rot[5],p3_rot[6],p3_rot[7],p3_rot[8]],
[p4_rot[4],p4_rot[5],p4_rot[6],p4_rot[7],p4_rot[8]],
[p5_rot[4],p5_rot[5],p5_rot[6],p5_rot[7],p5_rot[8]],
]
);
// 5、S*A_inverse
result = math.multiply(S_mat,A_inverse_mat);
return result;
}
function getRotationPrecomputeL(precompute_L, rotationMatrix){
let rotMatBand1 = computeSquareMatrix_3by3(rotationMatrix);
let rotMatBand2 = computeSquareMatrix_5by5(rotationMatrix);
let result = [];
for(let i = 0;i < 3;i++)
{
let rotSHBand1 = math.multiply(rotMatBand1, [precompute_L[i][1], precompute_L[i][2], precompute_L[i][3]]);
let rotSHBand2 = math.multiply(rotMatBand2, [precompute_L[i][4], precompute_L[i][5], precompute_L[i][6],
precompute_L[i][7], precompute_L[i][8]]);
result[i] = mat3.fromValues(precompute_L[i][0], rotSHBand1._data[0], rotSHBand1._data[1],
rotSHBand1._data[2], rotSHBand2._data[0], rotSHBand2._data[1],
rotSHBand2._data[2], rotSHBand2._data[3], rotSHBand2._data[4]);
}
return result;
}
已经有环境光照了。
let RGBMat3 = getMat3ValueFromRGB(precomputeL[guiParams.envmapId]);
// Bonus - Fast Spherical Harmonic Rotation
let precomputeL_RGBMat3 = getRotationPrecomputeL(RGBMat3, cameraModelMatrix);
这里在WebGLRenderer里对输入环境光格式做一下处理