1.点光源的光照值
光照来源于点,太阳也算是一个点光源,光照射到物体的同一面上有明有暗(即光照值不同),将面上的每个点到光源的矢量与法向量点乘得到的值就是光照值。
顶点着色器:
主要添加了光照位置的参数,并将顶点着色器中求出来的光照向量传值给着色器进行渲染。
<script id="vertex-shader-3D" type="x-shader/x-vertex">
attribute vec4 a_position;
attribute vec3 a_normal;
//光源位置
uniform vec3 u_lightWorldPosition;
uniform mat4 u_world;
uniform mat4 u_worldViewProjection;
uniform mat4 u_worldInverseTranspose;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
void main(){
gl_Position = u_worldViewProjection * a_position;
v_normal = mat3(u_worldInverseTranspose) * a_normal;
vec3 surfaceWorldPosition = (u_world * a_position).xyz;
v_surfaceToLight = u_lightWorldPosition - surfaceWorldPosition;
}
</script>
片段着色器(比较重要的就是点乘获取光照值进行渲染):
<script id="fragment-shader-3D" type="x-shader/x-fragment">
precision mediump float;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
uniform vec4 u_color;
void main() {
vec3 normal = normalize(v_normal);
vec3 surfaceToLightDirection = normalize(v_surfaceToLight);
float light = dot(normal, surfaceToLightDirection);
gl_FragColor = u_color;
gl_FragColor.rgb *= light;
}
</script>
获取参数以及设定
var MVPUniformLocation = webgl.getUniformLocation(program, "u_worldViewProjection");
var worldUniformLocation = webgl.getUniformLocation(program, "u_world");
var worldITUniformLocation = webgl.getUniformLocation(program, "u_worldInverseTranspose");
var lightPUniformLocation = webgl.getUniformLocation(program, "u_lightWorldPosition");
//投影矩阵
var projectionMatrix = m4.perspective(fieldofViewInRadians, webgl.canvas.clientWidth / webgl.canvas.clientHeight, 1, 2000);
var cameraPosition = [100, 150, 200];
var targetPosition = [0, 35, 0];
var up = [0, 1, 0];
//相机矩阵
var cameraMatrix = m4.lookAt(cameraPosition, targetPosition, up);
//视图矩阵
var viewMatrix = m4.inverse(cameraMatrix);
//视图投影矩阵
var viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix);
//模型矩阵
var worldMatrix = m4.yRotation(fRotationRadians);
var worldInverseMatrix = m4.inverse(worldMatrix);
var worldInverseTransposeMatrix = m4.transpose(worldInverseMatrix);
//模型视图投影矩阵
var worldViewProjectionMatrix = m4.multiply(viewProjectionMatrix, worldMatrix);
webgl.uniformMatrix4fv(MVPUniformLocation, false, worldViewProjectionMatrix);
webgl.uniformMatrix4fv(worldITUniformLocation, false, worldInverseTransposeMatrix);
webgl.uniformMatrix4fv(worldUniformLocation, false, worldMatrix);
//设置光照方向,光源来源
webgl.uniform3fv(lightPUniformLocation, [20, 30, 60]);
结果如下:
2.镜面反射
如果入射角和反射角恰好与眼睛和光源的夹角相同,那么光线就会反射到眼前且会特别亮,就如镜子一般。将halfVector向量与物体表面法向量点乘,+1表示方向一致,0表示垂直,-1表示方向相反;方向一致时,光就会进入人眼。
顶点着色器(加了个相机位置跟物体表面到相机的向量):
<script id="vertex-shader-3D" type="x-shader/x-vertex">
attribute vec4 a_position;
attribute vec3 a_normal;
//光源位置
uniform vec3 u_lightWorldPosition;
//相机位置
uniform vec3 u_viewWorldPosition;
uniform mat4 u_world;
uniform mat4 u_worldViewProjection;
uniform mat4 u_worldInverseTranspose;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;
void main(){
gl_Position = u_worldViewProjection * a_position;
v_normal = mat3(u_worldInverseTranspose) * a_normal;
vec3 surfaceWorldPosition = (u_world * a_position).xyz;
v_surfaceToLight = u_lightWorldPosition - surfaceWorldPosition;
v_surfaceToView = u_viewWorldPosition - surfaceWorldPosition;
}
</script>
片段着色器(主要是halfVector和specular):
<script id="fragment-shader-3D" type="x-shader/x-fragment">
precision mediump float;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;
uniform vec4 u_color;
void main() {
vec3 normal = normalize(v_normal);
vec3 surfaceToLightDirection = normalize(v_surfaceToLight);
vec3 surfaceToViewDirection = normalize(v_surfaceToView);
vec3 halfVector = normalize(surfaceToLightDirection + surfaceToViewDirection);
float light = dot(normal, surfaceToLightDirection);
float specular = dot(normal,halfVector);
gl_FragColor = u_color;
gl_FragColor.rgb *= light;
//加上高光
gl_FragColor.rgb += specular;
}
</script>
参数设置:
var viewPUniformLocation = webgl.getUniformLocation(program, "u_viewWorldPosition");
webgl.uniform3fv(viewPUniformLocation, cameraPosition);
结果如下:
太亮啦!那我们调整一下吧
3. 镜面光的优化
将高光从线性变换变成指数变换,即 y = pow(x,a),a越大,图中阴影面积就越小,越接近指数变换,a越小阴影面积就越接近整个矩形,这样光照就变强了。
只用修改片段着色器:
light的点乘结果有可能为赋值,这里只取正值做高光的计算,如果为负的话,高光设为0
<script id="fragment-shader-3D" type="x-shader/x-fragment">
precision mediump float;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;
uniform vec4 u_color;
uniform float u_shininess;
void main() {
vec3 normal = normalize(v_normal);
vec3 surfaceToLightDirection = normalize(v_surfaceToLight);
vec3 surfaceToViewDirection = normalize(v_surfaceToView);
vec3 halfVector = normalize(surfaceToLightDirection + surfaceToViewDirection);
float light = dot(normal, surfaceToLightDirection);
//float specular = dot(normal,halfVector);
float specular = 0.0;
if(light>0.0){
specular = pow(dot(normal,halfVector),u_shininess);
}
gl_FragColor = u_color;
gl_FragColor.rgb *= light;
gl_FragColor.rgb += specular;
}
</script>
设置参数:
var shininessUniformLocation = webgl.getUniformLocation(program, "u_shininess");
var shininess = 150;
webgl.uniform1f(shininessUniformLocation, shininess);
结果如下:
看 仿佛一个小灯。
4. 光源颜色的修改
片段着色器(加了光源颜色和高光颜色):
<script id="fragment-shader-3D" type="x-shader/x-fragment">
precision mediump float;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;
uniform vec4 u_color;
uniform float u_shininess;
uniform vec3 u_lightColor;
uniform vec3 u_specularColor;
void main() {
vec3 normal = normalize(v_normal);
vec3 surfaceToLightDirection = normalize(v_surfaceToLight);
vec3 surfaceToViewDirection = normalize(v_surfaceToView);
vec3 halfVector = normalize(surfaceToLightDirection + surfaceToViewDirection);
float light = dot(normal, surfaceToLightDirection);
//float specular = dot(normal,halfVector);
float specular = 0.0;
if(light>0.0){
specular = pow(dot(normal,halfVector),u_shininess);
}
gl_FragColor = u_color;
gl_FragColor.rgb *= light * u_lightColor;
gl_FragColor.rgb += specular * u_specularColor;
}
</script>
参数设置:
var lightColorUnifromLoation = webgl.getUniformLocation(program, "u_lightColor");
var specularColorUniformLocation = webgl.getUniformLocation(program, "u_specularColor");
webgl.uniform3fv(lightColorUnifromLoation, m4.normalize([1, 0.6, 0.6]));
webgl.uniform3fv(specularColorUniformLocation, m4.normalize([1, 0.2, 0.2]));
结果如下:
将~暖光来了。