Three.js 灯光模型的计算如何在着色器程序中工作?
我正在努力阅读本教程: 但我无法跟进。基本上,代码通过使用直接在GPU上运行的着色器来创建平行光。代码如下:Three.js 灯光模型的计算如何在着色器程序中工作?,three.js,glsl,webgl,lighting,light,Three.js,Glsl,Webgl,Lighting,Light,我正在努力阅读本教程: 但我无法跟进。基本上,代码通过使用直接在GPU上运行的着色器来创建平行光。代码如下: // same name and type as VS varying vec3 vNormal; void main() { // calc the dot product and clamp // 0 -> 1 rather than -1 -> 1 vec3 light = vec3(0.5,0.2,1.0); // ensure
// same name and type as VS
varying vec3 vNormal;
void main() {
// calc the dot product and clamp
// 0 -> 1 rather than -1 -> 1
vec3 light = vec3(0.5,0.2,1.0);
// ensure it's normalized
light = normalize(light);
// calculate the dot product of
// the light to the vertex normal
float dProd = max(0.0, dot(vNormal, light));
// feed into our frag colour
gl_FragColor = vec4(dProd, dProd, dProd, 1.0);
}
具体来说,我不明白的是:
float dProd = max(0.0, dot(vNormal, light));
顶点法线与光的点积如何创建平行光。谁能用图表给我解释一下吗。我无法得到它。这看起来有点神奇。我知道在这个顶点着色器中,每个顶点都被作为一个输入传递,这个输入称为法线,因为它用1表示,共享变量然后在上面的片段着色器代码中使用。但除此之外,我不明白它是如何工作的
注:我本可以问博客作者,但据我所知,他正在休假两周。所以我想,有一些物理或3.js经验的人可能会告诉我。Lambertian反射模型
利用双向反射分布函数BRDF对计算机图形学中的光反射进行建模。
BRDF是一个函数,它给出沿输出方向反射的光与从输入方向入射的光之间的关系
完美漫反射曲面的BRDF对于所有入射和传出方向具有相同的值。这大大减少了计算量,因此通常用于对漫反射曲面建模,因为它在物理上是合理的,即使真实世界中没有纯漫反射材质。这种BRDF被称为朗伯反射,因为它遵循朗伯余弦定律
朗伯反射通常用作漫反射的模型。该技术使所有闭合多边形(如三维网格中的三角形)在渲染时在所有方向上均匀反射光。扩散系数是从法向量和光向量之间的角度计算的
f_Lambertian = max( 0.0, dot( N, L )
其中N是曲面的法向量,L是朝向光源的向量
工作原理
通常,两个矢量的点积等于两个矢量之间的夹角的余弦乘以两个矢量的幅值长度
dot( A, B ) == length( A ) * length( B ) * cos( angle_A_B )
因此,2个单位向量的点积等于2个向量之间夹角的余弦,因为单位向量的长度为1
uA = normalize( A )
uB = normalize( B )
cos( angle_A_B ) == dot( uA, uB )
如果我们看一下-90°和90°角之间的cosx函数,那么我们可以看到,在0°角时,它的最大值为1,在90°和-90°角时,它下降到0
这种行为正是我们想要的反射模型。当表面的法线方向和光源方向在同一方向上,两者之间的夹角为0°时,我们需要最大反射。
相反,如果向量a正交化,两者之间的角度为90°,则我们希望反射最小,并且希望在0°和90°的两个边界之间平滑且连续的函数运行
如果在顶点着色器中计算灯光模型,则将为基本体的每个角点计算反射。在基本体之间,反射根据其重心坐标进行插值。
请参见球面上产生的反射:
另见:
WebGL示例:球体上的朗伯漫反射
函数加载场景{
var gl、progDraw、vp_尺寸;
var bufSphere={};
函数renderdelteMS{
照相机。创建;
Camera.vp=vp_尺寸;
总图视口0,0,vp_大小[0],vp_大小[1];
gl.enable gl.DEPTH\U测试;
gl.clearColor 0.0,0.0,0.0,1.0;
gl.clear gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT;
//设置绘图着色器
着色器程序。使用progDraw.prog;
ShaderProgram.SetUniformM44 progDraw.prog,u_projectionMat44,Camera.Perspective;
ShaderProgram.SetUniformM44 progDraw.prog,u_viewMat44,Camera.LookAt;
var modelMat=IdentityMat44
modelMat=旋转轴modelMat,CalcAng delteMS,13.0,0;
modelMat=旋转轴modelMat,CalcAng delteMS,17.0,1;
ShaderProgram.SetUniformM44 progDraw.prog,u_modelMat44,modelMat;
ShaderProgram.SetUniformF3 progDraw.prog,u_颜色,[1.0,0.5,0.0];
ShaderProgram.SetUniformF3 progDraw.prog,u_lightDir,[-4.0,0.0,-1.0]
//画场景
顶点缓冲区。绘制bufSphere;
requestAnimationFramerender;
}
函数调整大小{
//vp_大小=[gl.drawingBufferWidth,gl.drawingBufferHeight];
vp_size=[window.innerWidth,window.innerHeight]
canvas.width=vp_大小[0];
canvas.height=vp_大小[1];
}
函数初始化场景{
canvas=document.getElementById画布;
gl=canvas.getContext实验webgl;
如果!德国劳埃德船级社
返回null;
progDraw={}
progDraw.prog=ShaderProgram.Create
[{source:draw shader vs,stage:gl.VERTEX_shader},
{sourc
e:绘制着色器fs,阶段:gl.FRAGMENT_shader}
] ;
如果progDraw.prog
返回null;
progDraw.inPos=gl.getAttributeLocation progDraw.prog,inPos;
//创建球体
变量层大小=16,周长大小=32;
var rad_环=1.0;
var rad_管=0.5;
var sphere_pts=[];
球体压入0.0,0.0,-1.0;
对于var i_l=1;i\u l<层大小;++我{
var angH=1.0-i_l/层大小*Math.PI;
var h=Math.cos angH;
var r=数学中的sin angH;
对于变量i_c=0;i_c<周长;++i_c{
var-contracx=Math.cos2*Math.PI*i_c/contracu size;
var-circuy=Math.sin2*Math.PI*i_c/circusize;
球点推力r*圆周,r*圆周,h;
}
}
球体按0.0,0.0,1.0;
var sphere_inx=[];
对于var i_c=0;i_c<圆周尺寸;++i_c{
球体inx.push i_c+1,0,i_c+1%圆周大小+1
}
对于var i_l=0;i_l<层大小-2;++我{
var l1=i_l*圆周尺寸+1;
变量l2=i_l+1*周长+1
对于变量i_c=0;i_c<周长;++i_c{
var i_n=i_c+1%的周长;
球体inx.push l1+i_c,l1+i_n,l2+i_c,l1+i_n,l2+i_n,l2+i_c;
}
}
对于var i_c=0;i_c<圆周尺寸;++i_c{
变量i_开始=1+层大小-2*周长大小;
var i_n=i_c+1%的周长;
sphere_inx.push i_start+i_c,i_start+i_n,sphere_pts.length/3-1;
}
bufSphere=VertexBuffer.Create
[{data:sphere_pts,attrSize:3,attrLoc:progDraw.inPos}],
球体;
window.onresize=调整大小;
调整大小;
requestAnimationFramerender;
}
函数Fract val{
返回val-Math.trunc val;
}
函数计算时间间隔{
返回分数deltaTime/1000*区间*2.0*Math.PI;
}
函数CalcMove deltaTime,间隔,范围{
变量位置=自分形增量/1000*区间*2.0
var pos=pos<1.0?pos:2.0-pos
返回范围[0]+范围[1]-范围[0]*pos;
}
函数椭圆位置a、b、angRag{
变量a_b=a*a-b*b
var ea=a_b=0?0:Math.sqrt-a_b;
返回[a*Math.sin angRag-ea,b*Math.cos angRag-eb,0];
}
glArrayType=浮点数组的类型=未定义?Float32Array:WebGLFloatArray的类型!=未定义?WebGLFloatArray:数组;
函数标识MT44{
var m=新Glarray类型16;
m[0]=1;m[1]=0;m[2]=0;m[3]=0;
m[4]=0;m[5]=1;m[6]=0;m[7]=0;
m[8]=0;m[9]=0;m[10]=1;m[11]=0;
m[12]=0;m[13]=0;m[14]=0;m[15]=1;
返回m;
};
函数RotateAxismatA,angRad,轴{
var-aMap=[[1,2],[2,0],[0,1];
var a0=aMap[axis][0],a1=aMap[axis][1];
var sinAng=Math.sinangRad,cosAng=Math.cosangRad;
var matB=新Glarray类型16;
对于变量i=0;i<16;+i matB[i]=matA[i];
对于变量i=0;i<3;++i{
matB[a0*4+i]=matA[a0*4+i]*cosAng+matA[a1*4+i]*sinAng;
matB[a1*4+i]=matA[a0*4+i]*-sinAng+matA[a1*4+i]*cosAng;
}
返回matB;
}
函数交叉a,b{返回[a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0],0.0];}
函数点a,b{返回a[0]*b[0]+a[1]*b[1]+a[2]*b[2];}
函数规范化{
var len=Math.sqrt v[0]*v[0]+v[1]*v[1]+v[2]*v[2];
返回[v[0]/len,v[1]/len,v[2]/len];
}
var-Camera={};
Camera.create=函数{
this.pos=[0,1.5,0.0];
this.target=[0,0,0];
this.up=[0,0,1];
这个.fov_y=90;
this.vp=[800600];
该值为0.5;
这个.far=100.0;
}
照相机。透视=功能{
var fn=this.far+this.near;
var f_n=this.far-this.near;
var r=this.vp[0]/this.vp[1];
var t=1/Math.tan Math.PI*this.fov_y/360;
var m=识别码44;
m[0]=t/r;m[1]=0;m[2]=0;m[3]=0;
m[4]=0;m[5]=t;m[6]=0;m[7]=0;
m[8]=0;m[9]=0;m[10]=-fn/f_n;m[11]=-1;
m[12]=0;m[13]=0;m[14]=-2*this.far*this.near/f_n;m[15]=0;
返回m;
}
Camera.LookAt=函数{
var mz=Normalize[this.pos[0]-this.target[0]、this.pos[1]-this.target[1]、this.pos[2]-this.target[2];
var mx=标准化交叉点,mz;
var my=标准化交叉mz,mx;
var tx=点mx,this.pos;
var ty=点my,this.pos;
var tz=Dot[-mz[0]、-mz[1]、-mz[2]、this.pos;
var m=识别码44;
m[0]=mx[0];m[1]=my[0];m[2]=mz[0];m[3]=0;
m[4]=mx[1];m[5]=my[1];m[6]=
mz[1];m[7]=0;
m[8]=mx[2];m[9]=我的[2];m[10]=mz[2];m[11]=0;
m[12]=tx;m[13]=ty;m[14]=tz;m[15]=1;
返回m;
}
var ShaderProgram={};
ShaderProgram.Create=函数shaderList{
var shaderObjs=[];
对于变量i_sh=0;i_sh