多种光的漫反射

单独使用一种光,并未得到真正的明暗变化效果,要使能实现真实的立体效果,就要多种光结合在一起,这里同样展出在 Vertex Shader 和 Fragment Shader 中计算的不同效果。立方体的基底色是白色。

// 在顶点着色器中计算 <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_AmbientLight_color; uniform vec3 u_DirectionlLight_color; uniform vec3 u_LightDirection; uniform vec3 u_PointLight_color; uniform vec3 u_PointLight_position; varying vec4 v_Color; void main(){ gl_Position = u_mvpMatrix * a_Position; // 环境光 vec3 ambient = u_AmbientLight_color * a_Color.rgb; // 方向光 vec3 normal = normalize( vec3( a_Normal ) ); float cosAngle_D = max( dot( u_LightDirection, normal ), 0.0 ); vec3 directionalLight = u_DirectionlLight_color * a_Color.rgb * cosAngle_D; // 点光源 vec4 vertexPosition = a_Position; vec3 pointLightDirection = normalize( u_PointLight_position - vec3( vertexPosition ) ); float cosAngle_P = max( dot( pointLightDirection, normal ), 0.0 ); vec3 pointLight = u_PointLight_color * a_Color.rgb * cosAngle_P; v_Color = vec4( ambient + directionalLight + pointLight, 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 = a_Position.xyz; v_Color = a_Color; } </script> <script id="fragmentShader" type="x-shader/x-fragment"> precision mediump float; uniform vec3 u_AmbientLight_color; uniform vec3 u_DirectionlLight_color; uniform vec3 u_LightDirection; uniform vec3 u_PointLight_color; uniform vec3 u_PointLight_position; varying vec4 v_Normal; varying vec3 v_Position; varying vec4 v_Color; void main(){ // 环境光 vec3 ambient = u_AmbientLight_color * v_Color.rgb; // 方向光 vec3 normal = normalize( vec3( v_Normal ) ); float cosAngle_D = max( dot( u_LightDirection, normal ), 0.0 ); vec3 directionalLight = u_DirectionlLight_color * v_Color.rgb * cosAngle_D; // 点光源 vec3 vertexPosition = v_Position; vec3 pointLightDirection = normalize( u_PointLight_position - vertexPosition ); float cosAngle_P = max( dot( pointLightDirection, normal ), 0.0 ); vec3 pointLight = u_PointLight_color * v_Color.rgb * cosAngle_P; gl_FragColor = vec4( ambient + directionalLight + pointLight, v_Color.a ); } </script> // JavaScript 都一样 function init(){ ... ... gl.clearColor( 0.0, 0.0, 0.0, 1.0 ); gl.enable( gl.DEPTH_TEST ); var u_mvpMatrix = gl.getUniformLocation(gl.program, "u_mvpMatrix"); var u_AmbientLight_color = gl.getUniformLocation( gl.program, "u_AmbientLight_color" ); var u_DirectionlLight_color = gl.getUniformLocation( gl.program, "u_DirectionlLight_color" ); var u_LightDirection = gl.getUniformLocation( gl.program, "u_LightDirection" ); var u_PointLight_color = gl.getUniformLocation( gl.program, "u_PointLight_color" ); var u_PointLight_position = gl.getUniformLocation( gl.program, "u_PointLight_position" ); if (!u_mvpMatrix || !u_AmbientLight_color || !u_DirectionlLight_color || !u_LightDirection || !u_PointLight_color || !u_PointLight_position) { console.log('Failed to get the storage location'); console.log(u_mvpMatrix, u_AmbientLight_color, u_DirectionlLight_color, u_LightDirection, u_PointLight_color, u_PointLight_position); return; } var viewProjMatrix = new Matrix4(); viewProjMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100); viewProjMatrix.lookAt(3, 3, 7, 0, 0, 0, 0, 1, 0); gl.uniformMatrix4fv(u_mvpMatrix, false, viewProjMatrix.elements); var lightsData = { "ambient_color": [ u_AmbientLight_color, (new Vector3([ 0.5, 0.2, 0.1 ]).elements ) ], "directional_color": [ u_DirectionlLight_color, (new Vector3([ 0.4, 0.2, 0.2 ]).elements ) ], "light_direction": [ u_LightDirection, (new Vector3([ -1.0, 1.0, 1.0 ]).normalize().elements ) ], "point_color": [ u_PointLight_color, (new Vector3([ 1.0, 1.0, 1.0 ]).elements ) ], "point_position": [ u_PointLight_position, (new Vector3([ -2.0, 3.0, 4.0 ]).elements ) ] }; initUniformValue( gl, lightsData ); // 立方体 gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT ); var n = initVertexBuffers(gl); gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0); } function initUniformValue( gl, data ){ for( var section in data ){ var nodeInfo = data[section]; var location = nodeInfo[0], value = nodeInfo[1]; gl.uniform3fv( location, value ); } } function initVertexBuffers(gl){ ... ... }

在光照下旋转

如果已经使用了旋转矩阵对物体顶点坐标进行变换,但却没有重新计算光照时,会导致物体本来的各个面就这样,跟光照没有关系。

而要实现真实的旋转效果,即光对应的那个方向是不变的,就要用到 逆矩阵 来重新计算法向了。

<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 vec4 v_Position; varying vec4 v_Color; void main(){ gl_Position = u_mvpMatrix * a_Position; v_Normal = normalize( a_Normal ); v_Position = a_Position; v_Color = a_Color; } </script> <script id="fragmentShader" type="x-shader/x-fragment"> precision mediump float; uniform mat4 u_ModelMatrix; uniform mat4 u_NormalMatrix; uniform vec3 u_AmbientLight_color; uniform vec3 u_DirectionlLight_color; uniform vec3 u_LightDirection; uniform vec3 u_PointLight_color; uniform vec3 u_PointLight_position; varying vec4 v_Normal; varying vec4 v_Position; varying vec4 v_Color; void main(){ // 环境光 vec3 ambient = u_AmbientLight_color * v_Color.rgb; // 方向光 vec3 normal = normalize( vec3( u_NormalMatrix * v_Normal ) ); float cosAngle_D = max( dot( u_LightDirection, normal ), 0.0 ); vec3 directionalLight = u_DirectionlLight_color * v_Color.rgb * cosAngle_D; // 点光源 vec3 vertexPosition = vec3( u_ModelMatrix * v_Position ); vec3 pointLightDirection = normalize( u_PointLight_position - vertexPosition ); float cosAngle_P = max( dot( pointLightDirection, normal ), 0.0 ); vec3 pointLight = u_PointLight_color * v_Color.rgb * cosAngle_P; gl_FragColor = vec4( ambient + directionalLight + pointLight, v_Color.a ); } </script> function init(){ ... ... gl.clearColor( 0.0, 0.0, 0.0, 1.0 ); gl.enable( gl.DEPTH_TEST ); var u_mvpMatrix = gl.getUniformLocation( gl.program, "u_mvpMatrix" ); var u_ModelMatrix = gl.getUniformLocation( gl.program, "u_ModelMatrix" ); var u_NormalMatrix = gl.getUniformLocation( gl.program, "u_NormalMatrix" ); var u_AmbientLight_color = gl.getUniformLocation( gl.program, "u_AmbientLight_color" ); var u_DirectionlLight_color = gl.getUniformLocation( gl.program, "u_DirectionlLight_color" ); var u_LightDirection = gl.getUniformLocation( gl.program, "u_LightDirection" ); var u_PointLight_color = gl.getUniformLocation( gl.program, "u_PointLight_color" ); var u_PointLight_position = gl.getUniformLocation( gl.program, "u_PointLight_position" ); if (!u_mvpMatrix || !u_ModelMatrix || !u_NormalMatrix || !u_AmbientLight_color || !u_DirectionlLight_color || !u_LightDirection || !u_PointLight_color || !u_PointLight_position) { console.log("Failed to get the storage location'); console.log(u_mvpMatrix, u_ModelMatrix, u_NormalMatrix, u_AmbientLight_color, u_DirectionlLight_color, u_LightDirection, u_PointLight_color, u_PointLight_position); return; } var viewProjMatrix = new Matrix4(); viewProjMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100); viewProjMatrix.lookAt(3, 3, 7, 0, 0, 0, 0, 1, 0); gl.uniformMatrix4fv(u_mvpMatrix, false, viewProjMatrix.elements); var lightsData = { "ambient_color": [ u_AmbientLight_color, (new Vector3([ 0.5, 0.2, 0.1 ]).elements ) ], "directional_color": [ u_DirectionlLight_color, (new Vector3([ 0.4, 0.2, 0.2 ]).elements ) ], "light_direction": [ u_LightDirection, (new Vector3([ -1.0, 1.0, 1.0 ]).normalize().elements ) ], "point_color": [ u_PointLight_color, (new Vector3([ 1.0, 1.0, 1.0 ]).elements ) ], "point_position": [ u_PointLight_position, (new Vector3([ -2.0, 3.0, 4.0 ]).elements ) ] }; initUniformValue( gl, lightsData ); // 立方体 var n = initVertexBuffers(gl); var angle_step = 1/2; var modelMatrix = new Matrix4(), normalMatrix = new Matrix4(), mvpMatrix = new Matrix4(); animate(); function animate(){ changeAngle( gl ); gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT ); gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0); requestAnimationFrame(animate); } function changeAngle( gl, location ){ modelMatrix.rotate( angle_step, 0, 1, 0); gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements); mvpMatrix.set( viewProjMatrix).multiply( modelMatrix ); gl.uniformMatrix4fv(u_mvpMatrix, false, mvpMatrix.elements); normalMatrix.setInverseOf( modelMatrix ); // 逆矩阵 normalMatrix.transpose(); gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements); } }