OpenGL ES for Android(光照贴图)

597 阅读3分钟

简介

现实世界的物体各种各样,不同物体会对光产生不同的反应。陶瓷,铁片之类的物体会比墙壁,木柜的反射更强。想要模拟现实世界的光照效果就要了解物体对光线的反应效果。在我们的着色器代码中,我们定义一个结构体:Material,来表示材质。在上一节中,我们指定了一个物体和光的颜色,以及结合环境光和镜面强度分量,来定义物体的视觉输出。当描述一个物体的时候,我们可以用这三个分量来定义一个材质颜色(Material Color):环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)和镜面光照(Specular Lighting),再加上反光度(Shininess)来表示物体对光线的反应。

漫反射贴图

现在我们使用纹理来代替前面的纯色实现立方体的面,然后显示光照的效果。我们使用下面这样一幅图片来作为立方体的面,

这样显示出来的效果应该类似一个木箱。我们修改上篇的代码,在材质结构图中,把diffuse定义为sampler2D,并去掉ambient,且不再定义物体的颜色:

  struct Material {
      sampler2D diffuse;
      vec3 specular;
      float shininess;
  };

计算结果时使用纹理的像素颜色(不包括透明度)来计算:

    // 环境光照
    vec3 ambient = light.ambient* texture2D(material.diffuse, TextCoord).rgb;
    // 漫反射光照
    // 归一化光源线
    vec3 lightDir = normalize(light.position - fragPos);
    float diff = max(dot(norm, lightDir), 0.1);
    vec3 diffuse = diff * light.diffuse * texture2D(material.diffuse, TextCoord).rgb;
 
    // 镜面光照
    vec3 viewDir = normalize(-fragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.1), material.shininess);
    vec3 specular = spec * light.specular * material.specular;
    // 结果,使用ambient,diffuse,specular相加则为结合的效果
    vec3 result = ambient + diffuse + specular;
 
    gl_FragColor = vec4(result, 1.0);


此时光照的效果图如下:



镜面光贴图 

这时已经非常接近现实世界的物体光照效果了,但是我们看到箱子每个点的光照效果都相同,但是事实上应该不相同。我们看到箱子的中间部分是木头,周围的边框是钢铁,钢铁的反射是比木头的反射大得多的,此时我们需要引入镜面光贴图。先看下使用的贴图图片:



先看下使用的贴图图片, 使用这张图片的像素点计算时,黑色的部分没有反光,越是接近白色反光越大(实际上木头也有反光度的,这里我们暂时设置为黑色了,也可以自己把黑色ps为灰色的测试)。在代码方面,同样的我们定义specular为sampler2D,在计算镜面光照时使用我们的镜面贴图计算,关键代码如下:

  struct Material {
      sampler2D diffuse;
      sampler2D specular;
      float shininess;
  };
  ...
  void main() {
      // 环境光照
      vec3 ambient = light.ambient* texture2D(material.diffuse, TextCoord).rgb;
      // 漫反射光照
      // 归一化光源线
      vec3 lightDir = normalize(light.position - fragPos);
      float diff = max(dot(norm, lightDir), 0.0);
      vec3 diffuse = diff * light.diffuse * texture2D(material.diffuse, TextCoord).rgb;
 
      // 镜面光照
      vec3 viewDir = normalize(-fragPos);
      vec3 reflectDir = reflect(-lightDir, norm);
      float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
      vec3 specular = spec * light.specular * texture2D(material.specular, TextCoord).rgb;
      // 结果
      vec3 result = ambient + diffuse + specular;
 
      gl_FragColor = vec4(result, 1.0);
  }


此时运行的效果如下,我们看到边缘的光照比较明显,中间的光线比较弱 :




PS:因为着色器的代码比较多,把着色器的代码放到raw资源文件下了(也可以放在assets下,只是引入方式不同),这里推荐安装GLSL的插件GLSL Support。

练习题中改变镜面光贴图的图片,图片如果是彩色的,箱子被照到也会显示彩色;添加放射光贴图,见源码运行效果。