跳转到内容

GLSL 编程/GLUT/纹理层

来自 Wikibooks,开放世界中的开放书籍
人类皮肤层。

本教程介绍了多纹理,即在着色器中使用多个纹理图像。

它扩展了纹理球教程中的着色器代码以处理多个纹理,并展示了一种组合它们的方法。如果您还没有阅读过这些教程,现在是一个很好的机会去阅读它们。

表面层

[编辑 | 编辑源代码]

许多真实表面(例如左侧图像中所示的人类皮肤)由不同颜色、透明度、反射率等的几层组成。如果最上面一层是不透明的并且不透射任何光线,这对于渲染表面来说并不重要。但是,在许多情况下,最上面一层是(半)透明的,因此表面的准确渲染必须考虑多层。

事实上,Phong 反射模型中包含的镜面反射(参见镜面高光教程)通常对应于反射光的透明层:人类皮肤上的汗水、水果上的蜡、嵌入色素颗粒的透明塑料等等。另一方面,漫反射对应于最上面透明层下面的层。

照明这种分层表面不需要层的几何模型:它们可以用单个、无限薄的多边形网格表示。但是,照明计算必须为不同的层计算不同的反射,并且必须考虑光在层之间的传输(当光进入层时和当光离开层时)。这种方法的示例包括 Nvidia 的“Dawn”演示(参见“GPU Gems”一书的第 3 章,该书可在网上获得)和 Nvidia 的“Human Head”演示(参见“GPU Gems 3”一书的第 14 章,该书也可在网上获得)。

这些过程的完整描述超出了本教程的范围。只需说,层通常与纹理图像相关联,以指定它们的特性。在这里,我们只展示如何使用两种纹理和一种特定组合方法。这个例子实际上与层无关,因此说明了多纹理比表面层有更多应用。

未点亮地球的地图。
太阳照射地球的地图。

亮暗地球

[编辑 | 编辑源代码]

由于人类活动,地球的未点亮一侧并不完全黑暗。相反,人造灯光标记了城市的位置和范围,如左图所示。因此,地球的漫反射照明不应该仅仅使太阳照射表面的纹理图像变暗,而应该实际将它与未点亮纹理图像混合。请注意,太阳照射的地球比未点亮一侧的人造灯光亮得多;但是,我们降低了这种对比度,以便展示夜间纹理。

着色器代码扩展了来自纹理球教程的代码以处理两个纹理图像,并使用漫反射教程中描述的计算来处理单个方向光源

根据该方程,漫反射照明水平levelOfLighting为 max(0, N·L)。然后,我们根据levelOfLighting混合白天纹理和夜间纹理的颜色。这可以通过将白天颜色乘以levelOfLighting,将夜间颜色乘以1.0 - levelOfLighting,然后将它们加起来以确定片段的颜色来实现。或者,可以使用内置的 GLSL 函数mixmix(a, b, w) = b*w + a*(1.0-w)),它可能更有效。因此,片段着色器可以是(再次使用我们在longitudeLatitude中计算纹理坐标的特定方法)

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

    vec4 nighttimeColor = texture2D(mytexture, longitudeLatitude);
    vec4 daytimeColor = texture2D(mytexture_sunlit, longitudeLatitude);

    gl_FragColor = mix(nighttimeColor, daytimeColor, levelOfLighting);
}

请注意,这种混合与透明度教程中讨论的 alpha 混合非常相似,只是我们在片段着色器内执行混合,并使用levelOfLighting而不是应该“覆盖”另一个纹理的纹理的 alpha 分量(即不透明度)。事实上,如果daytimeTexture指定了一个 alpha 分量(参见透明纹理教程),我们可以使用这个 alpha 分量将mytexture_sunlit混合到mytexture上。这将对应于一个部分透明的层,它位于一个不透明的层之上,该不透明的层在最上面一层透明的地方可见。

完整着色器代码

[编辑 | 编辑源代码]
varying float levelOfLighting;
varying vec4 texCoords;
uniform sampler2D mytexture;
uniform sampler2D mytexture_sunlit;

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

    vec4 nighttimeColor = texture2D(mytexture, longitudeLatitude);
    vec4 daytimeColor = texture2D(mytexture_sunlit, longitudeLatitude);

    gl_FragColor = mix(nighttimeColor, daytimeColor, levelOfLighting);
}
attribute vec3 v_coord;
attribute vec3 v_normal;
uniform mat4 m, v, p;
uniform mat3 m_3x3_inv_transp;
uniform mat4 v_inv;

varying float levelOfLighting; // the level of diffuse
  // lighting that is computed in the vertex shader
varying vec4 texCoords;

struct lightSource
{
  vec4 position;
  vec4 diffuse;
  vec4 specular;
  float constantAttenuation, linearAttenuation, quadraticAttenuation;
  float spotCutoff, spotExponent;
  vec3 spotDirection;
};
lightSource light0 = lightSource(
  vec4(2.0,  1.0,  1.0, 0.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)
);

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;

  if (light0.position.w == 0.0) // directional light
    {
      lightDirection = normalize(vec3(light0.position));
    }
  else // point or spot light (or other kind of light)
    {
      vec3 vertexToLightSource = vec3(light0.position - m * v_coord4);
      lightDirection = normalize(vertexToLightSource);
    }

  levelOfLighting = max(0.0, dot(normalDirection, lightDirection));
  texCoords = v_coord4;
  gl_Position = mvp * v_coord4;
}

恭喜!您已到达关于基本纹理的最后一个教程的结尾。我们已经了解了

  • 表面层如何影响材料的外观(例如人类皮肤、打蜡水果、塑料等)。
  • 在对代表地球的球体进行纹理化时,如何将未点亮一侧的人造灯光考虑在内。
  • 如何在着色器中实现此技术。
  • 这与将 alpha 纹理混合到第二个不透明纹理上的关系。

进一步阅读

[编辑 | 编辑源代码]

如果您还想了解更多

  • 关于基本纹理,您应该阅读纹理球教程
  • 关于漫反射,您应该阅读漫反射教程
  • 关于 alpha 纹理,您应该阅读透明纹理教程
  • 关于高级皮肤渲染,您可以阅读 Randima Fernando(编辑)于 2004 年由 Addison-Wesley 出版、由 Curtis Beeson 和 Kevin Bjorke 撰写的“GPU Gems”一书中由 Curtis Beeson 和 Kevin Bjorke 撰写的第 3 章“Dawn”演示中的皮肤”和 Hubert Nguyen(编辑)于 2007 年由 Addison-Wesley 出版、由 Eugene d’Eon 和 David Luebke 撰写的“GPU Gems 3”一书中由 Eugene d’Eon 和 David Luebke 撰写的第 14 章“用于逼真实时皮肤渲染的高级技术”,这些书可在网上网上获得。


< GLSL 编程/GLUT

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