点光源的漫反射

点光源从一点发出的光,向四面八方散射,那么到达物体表面的每一个点上的光都不一样,近的获得的光就多, 其实是因为光源点到达物体表面的这个点的矢量,跟法线的夹角小,余弦值大;远的获得的光就少,实际上是光线跟法线的夹角大。

所以要根据公式求每个点的颜色

漫反射颜色 = 入射光颜色 × 表面基底色 × cosθ

实际是求光源点到物体表面的各个点所连成的光线的矢量:光源点坐标 - 顶点坐标。 在GLSL ES 里可直接进行矢量运算。 在 Vertex Shader 或者 Fragment Shader 中声明两个类型是 vec3uniform 变量,存入光的颜色和位置。顶点外的其余的点,在光栅化时会自动进行插值,WebGL 系统内部计算。 但点光源跟 方向光环境光 不一样, 它可以用逐顶点来计算光照,也可以用逐片段来计算光照。也就是把上面的公式的运算过程写在顶点着色器中,和片元着色器中,效果是不一样的。 在片元着色器中颜色过渡会更平滑,而在顶点着色器中会看到对角线,因为立方体每个面由两个三角形拼接而成。在某些复杂的几何模型中, 可能差别会更大。

在 Vertex Shader中计算:求一个 varying 变量的值,再传到 Fragment Shader 中的 gl_FragColor。而在 Fragment Shader 中计算:uniform 变量在 Fragment Shader 中声明,计算好直接赋值给 gl_FragColor。

// 在顶点着色器中计算 <script id="vertexShader" type="x-shader/x-vertex"> attribute vec4 a_Position; attribute vec4 a_Color; attribute vec4 a_Normal; uniform mat4 u_mvpMatrix; uniform vec3 u_LightColor; uniform vec3 u_LightPosition; varying vec4 v_Color; void main(){ gl_Position = u_mvpMatrix * a_Position; vec3 normal = normalize( vec3( a_Normal ) ); vec4 vertexPosition = a_Position; vec3 lightDirection = normalize( u_LightPosition - vec3(vertexPosition) ); float cosAngle = max( dot( lightDirection, normal ), 0.0 ); vec3 diffuse = u_LightColor * a_Color.rgb * cosAngle; v_Color = vec4( diffuse, a_Color.a ); } </script> <script id="fragmentShader" type="x-shader/x-fragment"> precision mediump float; varying vec4 v_Color; void main(){ gl_FragColor = v_Color; } </script> // 在片元着色器中计算 <script id="vertexShader" type="x-shader/x-vertex"> attribute vec4 a_Position; attribute vec4 a_Color; attribute vec4 a_Normal; uniform mat4 u_mvpMatrix; varying vec4 v_Normal; varying vec3 v_Position; varying vec4 v_Color; void main(){ gl_Position = u_mvpMatrix * a_Position; v_Normal = normalize( a_Normal ); v_Position = vec3( a_Position ); v_Color = a_Color; } </script> <script id="fragmentShader" type="x-shader/x-fragment"> precision mediump float; uniform vec3 u_LightColor; uniform vec3 u_LightPosition; varying vec4 v_Normal; varying vec3 v_Position; varying vec4 v_Color; void main(){ vec3 lightDirection = normalize( u_LightPosition - v_Position ); float cosAngle = max( dot( lightDirection, vec3( v_Normal ) ), 0.0 ); vec3 diffuse = u_LightColor * v_Color.rgb * cosAngle; gl_FragColor = vec4( diffuse, v_Color.a ); } </script> // JavaScript 都一样 function init(){ var canvas = document.getElementById('canvas'), gl = canvas.getContext('webgl'); if(!gl){ console.log('Failed to get WebGL.'); return; } if(!initShaders(gl, "vertexShader", "fragmentShader")){ console.log('Failed to initShaders.'); return; } gl.clearColor( 0.0, 0.0, 0.0, 1.0 ); gl.enable( gl.DEPTH_TEST ); var viewProjMatrix = new Matrix4(); viewProjMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100); viewProjMatrix.lookAt(3, 3, 7, 0, 0, 0, 0, 1, 0); var u_mvpMatrix = gl.getUniformLocation(gl.program, 'u_mvpMatrix'); gl.uniformMatrix4fv(u_mvpMatrix, false, viewProjMatrix.elements); var lightColor = new Vector3([ 1.0, 1.0, 1.0 ]); var u_LightColor = gl.getUniformLocation( gl.program, "u_LightColor" ); gl.uniform3fv( u_LightColor, lightColor.elements ); var lightPosition = new Vector3([ 1.0, 2.0, 2.0 ]); var u_LightPosition = gl.getUniformLocation( gl.program, "u_LightPosition" ); gl.uniform3fv( u_LightPosition, lightPosition.elements ); // 立方体 gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT ); var n = initVertexBuffers(gl); gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0); } function initVertexBuffers(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 ]), colors = 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, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.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, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.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, 1.0 ]), 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, 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, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0 ]) , 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 ]); initArrayBuffer(gl, 'a_Position', vertices, 3, gl.FLOAT); initArrayBuffer(gl, 'a_Color', colors, 3, gl.FLOAT); initArrayBuffer(gl, 'a_Normal', normals, 3, gl.FLOAT); var indicesBuffer = gl.createBuffer(); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, indicesBuffer ); gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW ); return indices.length; } function initArrayBuffer(gl, attribute, data, num, type){ var buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); var a_location = gl.getAttribLocation(gl.program, attribute); gl.vertexAttribPointer(a_location, num, type, false, 0, 0); gl.enableVertexAttribArray(a_location); return true; }