前言
很久没有更新WebGL的内容了,这段时间除了学校的学习外,我把编程指南那本书给大致看完了,里面的效果70%也都实现了。本篇文章相比较之前会有比较大的跨越,具体可以往下继续看。
正式开始之前先贴一下运行效果:
代码里加了个交互,可以用鼠标对其进行x轴以及y轴旋转,能很清晰的看到模型表面颜色的变化
点光源:
点光源相比较平行光的区别在于点光源光的方向会随着模型变换而改变(平行光则为固定方向),那么如何得到光源方向就简单了,公式:光线方向 = 点光源坐标 - 模型顶点坐标。
一、环境光
我的理解就是模型所受到环境影响所反射出来的光。在这里我的代码很简单,就是直接设定好环境光的RGB值,那么公式:环境光 =环境光颜色 × 模型表面的颜色
uniform vec3 u_AmbientLight; //环境光颜色
vec3 ambient = u_AmbientLight * v_Color.rgb;
gl_FragColor +=ambient;
...
var u_AmbientLight = gl.getUniformLocation(gl.program,'u_AmbientLight');
gl.uniform3f(u_AmbientLight,0.3,0.3,0.3);
二、漫反射
漫反射的反射光在各个方向上是均匀的,所以任何角度看上去其强度是相同的。反射光的颜色取决于入射光的颜色、表面的基底色、入射光与表面形成的入射角,公式:漫反射光的颜色 = 入射光颜色 × 表面基底色 ×cos入射角
uniform vec3 u_LightColor;
...
float nDotL = max(dot(normal,lightDirection),0.0);
vec3 diffuse = u_LightColor * v_Color.rgb * nDotL;
gl_FragColor+=diffuse;
...
var u_LightColor = gl.getUniformLocation(gl.program,'u_LightColor');
gl.uniform3f(u_LightColor,1.0,1.0,1.0);
三、镜面反射
这个大家小的时候都感受到过,在有时会在物体表面看到非常刺眼的光,这就是镜面反射所造成的,也叫高光。
除了要考虑光源与点的距离之外,观察位置也是很重要的。那么镜面反射光的方向 = 观察位置 - 模型顶点坐标 。
ks为镜面反射系数,I为入射光强,r为光源到入射点距离,注意这里在max剔除大于90°的光之后,我们还乘了一个指数p,添加该项的原因很直接,因为离反射光越远就越不应该看见反射光,需要一个指数p加速衰减
红线越靠近顶部的位置越亮
这里有一个知识点为半程向量:
半程向量 =(光线方向+观察方向)/2,中间的夹角即为看到高光的角度范围。
最后:环境光 + 漫反射 + 镜面反射 = Blinn - Phong模型
下面是部分js代码:
var vsSource = `
attribute vec4 a_Position;
attribute vec4 a_Color;
attribute vec4 a_Normal;
uniform mat4 u_MvpMatrix;
uniform mat4 u_ModelMatrix;
uniform mat4 u_NormalMatrix;//变换法向量的矩阵
uniform vec3 u_viewWorldPosition;//眼睛位置
varying vec3 v_Normal;
varying vec3 v_Position;
varying vec3 v_surfaceToView;
varying lowp vec4 v_Color;
void main(void){
gl_Position = u_MvpMatrix * a_Position;
v_Position = vec3(u_ModelMatrix * a_Position);
v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));
v_surfaceToView = u_viewWorldPosition - v_Position;
v_Color = a_Color;
}
`;
var fsSource = `
precision mediump float;
uniform vec3 u_LightColor;
uniform vec3 u_LightPosition;//光源位置
uniform vec3 u_AmbientLight; //环境光颜色
uniform float u_shininess;
varying vec3 v_Normal;
varying vec3 v_Position;
varying vec3 v_surfaceToView;
varying lowp vec4 v_Color;
void main(void){
vec3 normal = normalize(v_Normal);
vec3 lightDirection = normalize(u_LightPosition - v_Position);
float nDotL = max(dot(normal,lightDirection),0.0);
vec3 surfaceToViewDirection = normalize(v_surfaceToView);
vec3 halfvector = normalize(surfaceToViewDirection + lightDirection);
float specular = 0.0;
if(nDotL >0.0){
specular = pow(dot(normal,halfvector),u_shininess);
} //这里是关于高光的部分
vec3 diffuse = u_LightColor * v_Color.rgb * nDotL;//漫反射
vec3 ambient = u_AmbientLight * v_Color.rgb;//环境光
gl_FragColor = vec4(diffuse + ambient + specular, v_Color.a);
}
`;
var gl;function main() {
var canvas = document.getElementById('glcanvas');
gl = canvas.getContext('webgl');
initShaders();
var n =initBuffers(gl);
var u_MvpMatrix = gl.getUniformLocation(gl.program,'u_MvpMatrix');
var u_LightColor = gl.getUniformLocation(gl.program,'u_LightColor');
var u_ModelMatrix = gl.getUniformLocation(gl.program,'u_ModelMatrix')
var u_LightPosition=gl.getUniformLocation(gl.program,'u_LightPosition')
var u_AmbientLight = gl.getUniformLocation(gl.program,'u_AmbientLight');
var u_NormalMatrix = gl.getUniformLocation(gl.program,'u_NormalMatrix');
//viewPosition
var u_viewWorldPosition=gl.getUniformLocation(gl.program,'u_viewWorldPosition');
var u_shininess = gl.getUniformLocation(gl.program,'u_shininess')
var shininess = 300;//高光系数
var camera = [15,3.5,5];//观察位置
gl.uniform3fv(u_viewWorldPosition,camera)
gl.uniform1f(u_shininess,shininess);
var modelMatrix = new Matrix4()gl.uniform3f(u_AmbientLight,0.3,0.3,0.3)
gl.uniform3f(u_LightColor,1.0,1.0,1.0);
// var lightDirection = new Vector3([0.5,3.0,4.0]);
// lightDirection.normalize();
// gl.uniform3fv(u_LightDirection,lightDirection.elements);
gl.uniform3f(u_LightPosition,2,3,1)
var mvpMatrix = new Matrix4();
mvpMatrix.setPerspective(20,1,1,100);
// mvpMatrix.multiply(modelMatrix)
mvpMatrix.lookAt(15,3.5,5,0,0,0,0,1,0);
}function initBuffers(gl) {
var vertices=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,
]);
var colors = 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,
0.4,1.0,0.4,0.4,1.0,0.4,0.4,1.0,0.4,0.4,1.0,0.4,
1.0,0.4,0.4,1.0,0.4,0.4,1.0,0.4,0.4,1.0,0.4,0.4,
0.4,1.0,1.0,0.4,1.0,1.0,0.4,1.0,1.0,0.4,1.0,1.0,
1.0,1.0,0.4,1.0,1.0,0.4,1.0,1.0,0.4,1.0,1.0,0.4,
1.0,0.4,1.0,1.0,0.4,1.0,1.0,0.4,1.0,1.0,0.4,1.0,
])
var indices = 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,//后
]);
var 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,
]);
var n = 8;
var position = gl.getAttribLocation(gl.program, 'a_Position');
var indexBuffer = gl.createBuffer();
// var FSIZE = vertices.BYTES_PER_ELEMENT;
// var FSIZE_c = colors.BYTES_PER_ELEMENT;
initArrayBuffer(gl,vertices,3,gl.FLOAT,'a_Position')
initArrayBuffer(gl,colors,3,gl.FLOAT,'a_Color')
initArrayBuffer(gl,normals,3,gl.FLOAT,'a_Normal')
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW)
return indices.length;
}
var g_mvpMatrix = new Matrix4();var g_normalMatrix = new Matrix4();function draw(gl,n,modelMatrix,u_ModelMatrix,u_NormalMatrix,mvpMatrix,u_MvpMatrix,currentAngle){
modelMatrix.setRotate(currentAngle[0],1.0,0.0,0.0);
modelMatrix.rotate(currentAngle[1],0.0,1.0,0.0);
gl.uniformMatrix4fv(u_ModelMatrix,false,modelMatrix.elements)g_mvpMatrix.set(mvpMatrix);
g_mvpMatrix.multiply(modelMatrix);
gl.uniformMatrix4fv(u_MvpMatrix,false,g_mvpMatrix.elements);
g_normalMatrix.setInverseOf(modelMatrix);
g_normalMatrix.transpose();
gl.uniformMatrix4fv(u_NormalMatrix,false,g_normalMatrix.elements)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawElements(gl.TRIANGLES,n,gl.UNSIGNED_BYTE,0);
}
补充一下,模型在变换时法线方向可能也会跟着改变,这就涉及到了逆转置矩阵。代码里用两行解决(即对模型矩阵进行设置,这样可以拿到变换后的法线):
g_normalMatrix.setInverseOf(modelMatrix);
g_normalMatrix.transpose();
总结
以上是实现Blinn-Phong模型的过程,其实有很多都没解释,比如顶点索引,mvp矩阵,法线和逐片元处理等,还有一些细节问题我觉得还是需要结合书来学习比较好。关于纹理的实现需要弄个服务器,我嫌太麻烦了就暂时不做了。后面的想法是进行一些webgl以及图形学的基础知识巩固,最最重要的是为明年秋招做准备,目前要做的是在webgis方向的一些gis的专业知识和原理以及前端js技术的需要进行学习。