跳转到内容

GLSL 编程/GLUT/光照纹理表面

来自 Wikibooks,开放世界中的开放书籍
从阿波罗8号看到的地球升起。

本教程涵盖了纹理表面的逐顶点光照

它结合了纹理球体教程镜面高光教程的着色器代码,以计算光照,其中漫射材质颜色由纹理确定。如果你还没有阅读过纹理球体教程镜面高光教程,现在是阅读它们的好时机。

纹理和漫射逐顶点光照

[编辑 | 编辑源代码]

纹理球体教程中,纹理颜色用作片段着色器的输出。但是,也可以使用纹理颜色作为光照计算中的任何参数,特别是用于漫射反射的材质常数,它在漫射反射教程中介绍。它出现在Phong 反射模型的漫射部分

其中此方程使用不同的材质常数分别对应于红色、绿色和蓝色的三个颜色分量。通过使用纹理来确定这些材质常数,它们可以在表面上变化。

着色器代码

[编辑 | 编辑源代码]

镜面高光教程中的逐顶点光照相比,这里的顶点着色器计算了两个不同的颜色:diffuseColor在片段着色器中与纹理颜色相乘,而specularColor只是镜面项,它不应该与纹理颜色相乘。这是有道理的,但由于历史原因(即能力较差的旧图形硬件),这有时被称为“独立镜面颜色”。

attribute vec3 v_coord;
attribute vec3 v_normal;
uniform mat4 m, v, p;
uniform mat3 m_3x3_inv_transp;
uniform mat4 v_inv;

varying vec3 diffuseColor;
  // the diffuse Phong lighting computed in the vertex shader
varying vec3 specularColor;
  // the specular Phong lighting computed in the vertex shader
varying vec4 texCoords; // the texture coordinates

struct lightSource
{
  vec4 position;
  vec4 diffuse;
  vec4 specular;
  float constantAttenuation, linearAttenuation, quadraticAttenuation;
  float spotCutoff, spotExponent;
  vec3 spotDirection;
};
lightSource light0 = lightSource(
  vec4(0.0,  1.0,  2.0, 1.0),
  vec4(1.0,  1.0,  1.0, 1.0),
  vec4(1.0,  1.0,  1.0, 1.0),
  0.0, 1.0, 0.0,
  180.0, 0.0,
  vec3(0.0, 0.0, 0.0)
);
vec4 scene_ambient = vec4(0.2, 0.2, 0.2, 1.0);

struct material
{
  vec4 ambient;
  vec4 diffuse;
  vec4 specular;
  float shininess;
};
material mymaterial = material(
  vec4(0.2, 0.2, 0.2, 1.0),
  vec4(1.0, 0.8, 0.8, 1.0),
  vec4(1.0, 1.0, 1.0, 1.0),
  5.0
);

void main(void)
{
  vec4 v_coord4 = vec4(v_coord, 1.0);
  mat4 mvp = p*v*m;
  vec3 normalDirection = normalize(m_3x3_inv_transp * v_normal);
  vec3 viewDirection = normalize(vec3(v_inv * vec4(0.0, 0.0, 0.0, 1.0) - m * v_coord4));
  vec3 lightDirection;
  float attenuation;

  if (light0.position.w == 0.0) // directional light
    {
      attenuation = 1.0; // no attenuation
      lightDirection = normalize(vec3(light0.position));
    }
  else // point or spot light (or other kind of light)
    {
      vec3 vertexToLightSource = vec3(light0.position - m * v_coord4);
      float distance = length(vertexToLightSource);
      lightDirection = normalize(vertexToLightSource);
      attenuation = 1.0 / (light0.constantAttenuation
                           + light0.linearAttenuation * distance
                           + light0.quadraticAttenuation * distance * distance);

      if (light0.spotCutoff <= 90.0) // spotlight
        {
          float clampedCosine = max(0.0, dot(-lightDirection, normalize(light0.spotDirection)));
          if (clampedCosine < cos(radians(light0.spotCutoff))) // outside of spotlight cone
            {
              attenuation = 0.0;
            }
          else
            {
              attenuation = attenuation * pow(clampedCosine, light0.spotExponent);
            }
        }
    }

  vec3 ambientLighting = vec3(scene_ambient);
    // without material color!

  vec3 diffuseReflection = attenuation
    * vec3(light0.diffuse)
    * max(0.0, dot(normalDirection, lightDirection));
    // without material color!

  vec3 specularReflection;
  if (dot(normalDirection, lightDirection) < 0.0) // light source on the wrong side?
    {
      specularReflection = vec3(0.0, 0.0, 0.0); // no specular reflection
    }
  else // light source on the right side
    {
      specularReflection = attenuation * vec3(light0.specular) * vec3(mymaterial.specular)
        * pow(max(0.0, dot(reflect(-lightDirection, normalDirection), viewDirection)),
              mymaterial.shininess);
    }

  diffuseColor = ambientLighting + diffuseReflection;
  specularColor = specularReflection;
  texCoords = v_coord4;
  gl_Position = mvp * v_coord4;
}

片段着色器用纹理颜色调制diffuseColor,并添加specularColor

varying vec3 diffuseColor;
    // the interpolated diffuse Phong lighting
varying vec3 specularColor;
    // the interpolated specular Phong lighting
varying vec4 texCoords;
    // the interpolated texture coordinates
uniform sampler2D mytexture;

void main(void)
{
    vec2 longitudeLatitude = vec2((atan(texCoords.y, texCoords.x) / 3.1415926 + 1.0) * 0.5,
                                  (asin(texCoords.z) / 3.1415926 + 0.5));
    // unusual processing of texture coordinates

    gl_FragColor = vec4(diffuseColor
        * vec3(texture2D(mytexture, longitudeLatitude))
        + specularColor, 1.0);
}

为了将纹理图像分配给此着色器,你应该按照纹理球体教程中讨论的步骤进行操作。

恭喜你,你已经完成了本教程。我们已经了解了

  • 纹理和逐顶点光照通常如何结合。
  • 什么是“独立镜面颜色”。

进一步阅读

[编辑 | 编辑源代码]

如果你想了解更多

  • 关于Phong 反射模型的漫射反射项,你应该阅读漫射反射教程
  • 关于逐顶点光照或Phong 反射模型的其余部分,即环境项和镜面项,你应该阅读镜面高光教程
  • 关于纹理的基础知识,你应该阅读纹理球体教程


< GLSL 编程/GLUT

除非另有说明,否则本页面上的所有示例源代码均属公有领域。
返回OpenGL 编程 - 光照部分 返回GLSL 编程 - GLUT 部分
华夏公益教科书