Please note, this is a STATIC archive of website developer.mozilla.org from November 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Lighting in WebGL

这篇翻译不完整。请帮忙从英语翻译这篇文章

在使用灯光之前,首先我们需要了解与定义更广泛的OpenGL不同,WebGL并没有继承OpenGL中灯光的支持。所以你只能由自己完全得控制灯光。幸运得是,这也并不是很难,本文接下来就会介绍完成灯光的基础。

在3D空间中模拟现实灯光

在3D空间中模拟现实世界的灯光的具体原理和细节绝非本篇文章能够描述清楚的,但是对灯光模型有一定的了解对我们的学习还是很有帮助的。虽然这里没办法深入讲解,但是维基百科中的Phong着色法给出了一个不错的概要介绍,其中包含了最常用的几种光照模型。

三种光源类型如下:

环境光 是一种可以渗透到场景的每一个角落的光。它是非方向光并且会均匀地照射物体的每一个面,无论这个面是朝向哪个方向的。

方向光 是一束从一个固定的方向照射过来的光。这种光的特点可以理解为好像是从一个很遥远的地方照射过来的,然后光线中的每一个光子与其它光子都是平等运动的。举个例子来说,阳光就可以认为是方向光。

点光源光 是指光线是从一个点发射出来的,是向着四面八方发射的。这种光在我们的现实生活中是最常被用到的。举个例子来说,电灯泡就是向各个方向发射光线的。

以我们的需要来看,我们会简化光照模型,只考虑简单的方向光和环境光,不会考虑任何镜面反射和点光源。这样的话,我们只需要在我们使用的环境光上加上照射到旋转立方体的方向光就可以了。在这里可以看到之前的旋转立方体的例子

虽然可以抛开了点光源和镜面反射,但是关于方向光还是有两点需要注意一下:

  1. 需要在每个顶点信息中加入面的朝向法线。这个法线是一个垂直于这个顶点所在平面的向量。
  2. 需要明确方向光的传播方向,可以使用一个方向向量来定义。

Then we update the vertex shader to adjust the color of each vertex, taking into account the ambient lighting as well as the effect of the directional lighting given the angle at which it's striking the face. We'll see how to do that when we look at the code for the shader.

Building the normals for the vertices

The first thing we need to do is generate the array of normals for all the vertices that comprise our cube. Since a cube is a very simple object, this is easy to do; obviously for more complex objects, calculating the normals will be more involved.

cubeVerticesNormalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);

var vertexNormals = [
  // Front
   0.0,  0.0,  1.0,
   0.0,  0.0,  1.0,
   0.0,  0.0,  1.0,
   0.0,  0.0,  1.0,
  
  // Back
   0.0,  0.0, -1.0,
   0.0,  0.0, -1.0,
   0.0,  0.0, -1.0,
   0.0,  0.0, -1.0,
  
  // Top
   0.0,  1.0,  0.0,
   0.0,  1.0,  0.0,
   0.0,  1.0,  0.0,
   0.0,  1.0,  0.0,
  
  // Bottom
   0.0, -1.0,  0.0,
   0.0, -1.0,  0.0,
   0.0, -1.0,  0.0,
   0.0, -1.0,  0.0,
  
  // Right
   1.0,  0.0,  0.0,
   1.0,  0.0,  0.0,
   1.0,  0.0,  0.0,
   1.0,  0.0,  0.0,
  
  // Left
  -1.0,  0.0,  0.0,
  -1.0,  0.0,  0.0,
  -1.0,  0.0,  0.0,
  -1.0,  0.0,  0.0
];

gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(vertexNormals), gl.STATIC_DRAW);

This should look pretty familiar by now; we create a new buffer, bind it to be the array we're working with, then send along our array of vertex normals into the buffer by calling bufferData().

Then we add the code to drawScene() to bind the normals array to a shader attribute so the shader code can get access to it:

gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);
gl.vertexAttribPointer(vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0);

Finally, we need to update the code that builds the uniform matrices to generate and deliver to the shader a normal matrix, which is used to transform the normals when dealing with the current orientation of the cube in relation to the light source:

var normalMatrix = mvMatrix.inverse();
normalMatrix = normalMatrix.transpose();
var nUniform = gl.getUniformLocation(shaderProgram, "uNormalMatrix");
gl.uniformMatrix4fv(nUniform, false, new WebGLFloatArray(normalMatrix.flatten()));

Update the shaders

Now that all the data the shaders need is available to them, we need to update the code in the shaders themselves.

The vertex shader

The first thing to do is update the vertex shader so it generates a shading value for each vertex based on the ambient lighting as well as the directional lighting. Let's take a look at the code:

<script id="shader-vs" type="x-shader/x-vertex">
  attribute highp vec3 aVertexNormal;
  attribute highp vec3 aVertexPosition;
  attribute highp vec2 aTextureCoord;

  uniform highp mat4 uNormalMatrix;
  uniform highp mat4 uMVMatrix;
  uniform highp mat4 uPMatrix;
  
  varying highp vec2 vTextureCoord;
  varying highp vec3 vLighting;

  void main(void) {
    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
    vTextureCoord = aTextureCoord;
    
    // Apply lighting effect
    
    highp vec3 ambientLight = vec3(0.6, 0.6, 0.6);
    highp vec3 directionalLightColor = vec3(0.5, 0.5, 0.75);
    highp vec3 directionalVector = vec3(0.85, 0.8, 0.75);
    
    highp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0);
    
    highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);
    vLighting = ambientLight + (directionalLightColor * directional);
  }
</script>

Once the position of the vertex is computed, and we obtain the coordinates of the texel corresponding to the vertex, we can work on computing the shading for the vertex.

The first thing we do is transform the normal based on the current position and orientation of the cube, by multiplying the vertex's normal by the normal matrix. We can then compute the amount of directional lighting that needs to be applied to the vertex by calculating the dot product of the transformed normal and the directional vector (that is, the direction from which the light is coming). If this value is less than zero, then we pin the value to zero, since you can't have less than zero light.

Once the amount of directional lighting is computed, we can generate the lighting value by taking the ambient light and adding in the product of the directional light's color and the amount of directional lighting to provide. As a result, we now have an RGB value that will be used by the fragment shader to adjust the color of each pixel we render.

The fragment shader

The fragment shader now needs to be updated to take into account the lighting value computed by the vertex shader:

<script id="shader-fs" type="x-shader/x-fragment">
  varying highp vec2 vTextureCoord;
  varying highp vec3 vLighting;
  
  uniform sampler2D uSampler;
  
  void main(void) {
    mediump vec4 texelColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
    
    gl_FragColor = vec4(texelColor.rgb * vLighting, texelColor.a);
  }
</script>

Here we fetch the color of the texel, just like we did in the previous example, but before setting the color of the fragment, we multiply the texel's color by the lighting value to adjust the texel's color to take into account the effect of our light sources.

And that's it!

View the complete code | Open this demo on a new page

Exercises for the reader

Obviously, this is a simple example, implementing basic per-vertex lighting. For more advanced graphics, you'll want to implement per-pixel lighting, but this will get you headed in the right direction.

You might also try experimenting with the direction of the light source, the colors of the light sources, and so forth.

文档标签和贡献者

 此页面的贡献者: Sincoyw
 最后编辑者: Sincoyw,